регулярное выражение для соответствующей даты в Perl - PullRequest
1 голос
/ 30 августа 2010

Я хочу сопоставить даты в следующем формате:

2010-08-27 02:11:36

т.е. yyyy-mm-dd hh:mm:ss.

Прямо сейчас я не очень точен в том, что дата действительно возможна, но просто в том, что она в правильном формате.

Возможные форматы, которые должны соответствовать (для этого примера)

2010
2010-08
2010-08-27
2010-08-27 02
2010-08-27 02:11
2010-08-27 02:11:36

В Perl, что может быть кратким регулярным выражением для этого?

У меня есть это (что работает, кстати)

/\d{4}(-\d{2}(-\d{2}( \d{2}(:\d{2}(:\d{2})?)?)?)?)?/

Может ли это быть улучшено с точки зрения производительности?

Ответы [ 6 ]

8 голосов
/ 30 августа 2010

Исходя из отсутствия группы захвата в течение года, я предполагаю, что вы заботитесь только о том, соответствует ли дата.

Я испробовал несколько различных шаблонов, связанных с одним из вашего вопроса и тем, который далулучшение на десять-пятнадцать процентов привело к отключению захвата, т.е. ,

/\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}(?::\d{2}(?::\d{2})?)?)?)?)?/

Документация perlre охватывает (?:...):

(?: шаблон)

(? Imsx-imsx: шаблон)

Это для кластеризации, а не захвата;он группирует подвыражения как (), но не делает обратных ссылок, как ().Так что

@fields = split(/\b(?:a|b|c)\b/)

похоже на

@fields = split(/\b(a|b|c)\b/)

, но не выплевывает дополнительные поля.Также дешевле не захватывать символы, если вам это не нужно.

Любые буквы между ? и : действуют как модификаторы флагов, как и (?imsx-imsx).Например,

/(?s-i:more.*than).*million/i

эквивалентно более многословному

/(?:(?s-i)more.*than).*million/i

Результат теста:

Rate      U   U/NC CH/NC/A CH/NC/A/U     CH  CH/NC   null
U         31811/s     --   -32%    -58%      -59%   -61%   -66%   -93%
U/NC      46849/s    47%     --    -38%      -39%   -42%   -50%   -90%
CH/NC/A   76119/s   139%    62%      --       -1%    -6%   -18%   -84%
CH/NC/A/U 76663/s   141%    64%      1%        --    -6%   -17%   -84%
CH        81147/s   155%    73%      7%        6%     --   -13%   -83%
CH/NC     92789/s   192%    98%     22%       21%    14%     --   -81%
null     481882/s  1415%   929%    533%      529%   494%   419%     --

Код:

#! /usr/bin/perl

use warnings;
use strict;

use Benchmark qw/ :all /;

sub option_chain {
  local($_) = @_;
  /\d{4}(-\d{2}(-\d{2}( \d{2}(:\d{2}(:\d{2})?)?)?)?)?/
}

sub option_chain_nocap {
  local($_) = @_;
  /\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}(?::\d{2}(?::\d{2})?)?)?)?)?/
}

sub option_chain_nocap_anchored {
  local($_) = @_;
  /\A\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}(?::\d{2}(?::\d{2})?)?)?)?)?\z/
}

sub option_chain_anchored_unrolled {
  local($_) = @_;
  /\A\d\d\d\d(-\d\d(-\d\d( \d\d(:\d\d(:\d\d)?)?)?)?)?\z/
}

sub simple_split {
  local($_) = @_;
  split /[ :-]/;
}

sub unrolled {
  local($_) = @_;
  grep defined($_), /\A (\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d) \z
                    |\A (\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d)        \z
                    |\A (\d\d\d\d)-(\d\d)-(\d\d) (\d\d)               \z
                    |\A (\d\d\d\d)-(\d\d)-(\d\d)                      \z
                    |\A (\d\d\d\d)-(\d\d)                             \z
                    |\A (\d\d\d\d)                                    \z
                    /x;
}

sub unrolled_nocap {
  local($_) = @_;
  grep defined($_), /\A \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \z
                    |\A \d\d\d\d-\d\d-\d\d \d\d:\d\d      \z
                    |\A \d\d\d\d-\d\d-\d\d \d\d           \z
                    |\A \d\d\d\d-\d\d-\d\d                \z
                    |\A \d\d\d\d-\d\d                     \z
                    |\A \d\d\d\d                          \z
                    /x;
}

sub id { $_[0] }

my @examples = (
  "xyz",
  "2010",
  "2010-08",
  "2010-08-27",
  "2010-08-27 02",
  "2010-08-27 02:11",
  "2010-08-27 02:11:36",
);

cmpthese -1 => {
  "CH"        => sub {                   option_chain $_ for @examples },
  "CH/NC"     => sub {             option_chain_nocap $_ for @examples },
  "CH/NC/A"   => sub {    option_chain_nocap_anchored $_ for @examples },
  "CH/NC/A/U" => sub { option_chain_anchored_unrolled $_ for @examples },
  "U"         => sub {                       unrolled $_ for @examples },
  "U/NC"      => sub {                 unrolled_nocap $_ for @examples },
  "null"      => sub {                             id $_ for @examples },
};
5 голосов
/ 30 августа 2010

Как насчет чего-то из Regexp :: Common :: time ?

3 голосов
/ 30 августа 2010

Ваше регулярное выражение просто отлично, за исключением отсутствующих якорей (если вы не хотите соответствовать 2008 в "abc200890"?).Предполагая, что вы хотите сопоставить всю строку:

/^\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}(?::\d{2}(?::\d{2})?)?)?)?)?\z/

(?:...) следует использовать, если вы на самом деле не хотите захваченных подстрок, что, я думаю, будет иметь место.

2 голосов
/ 30 августа 2010

Я бы использовал функцию разделения:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @dates = (
'2010',
'2010-08',
'2010-08-27',
'2010-08-27 02',
'2010-08-27 02:11',
'2010-08-27 02:11:36',
);

for (@dates) {
  my @list = split /[ :-]/;
  print Dumper(\@list);
}

вывод:

$VAR1 = [
          '2010'
        ];
$VAR1 = [
          '2010',
          '08'
        ];
$VAR1 = [
          '2010',
          '08',
          '27'
        ];
$VAR1 = [
          '2010',
          '08',
          '27',
          '02'
        ];
$VAR1 = [
          '2010',
          '08',
          '27',
          '02',
          '11'
        ];
$VAR1 = [
          '2010',
          '08',
          '27',
          '02',
          '11',
          '36'
        ];
1 голос
/ 30 августа 2010

Если вы хотите быстрее, отойдите от регулярных выражений и посмотрите на модули XS: Date :: Calc - хороший вариант

1 голос
/ 30 августа 2010

Это соответствует всем вышеперечисленным (но также и другим вещам - см. Комментарий!) И может быть немного легче для чтения:

/(\d{4})(-\d{2})?(\w{1}\d{2})?(:\d{2})?/
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...