Использование строковых масок в Perl - PullRequest
2 голосов
/ 26 января 2011

У меня есть программа, которая позволяет пользователю указать маску, например MM-DD-YYYY, и сравнить ее со строкой. В строке MM будет считаться месяцем, DD будет днем ​​месяца, а YYYY будет годом. Все остальное должно точно соответствовать:

  • Строка: 31.12.2010 Маска MM-ДД-ГГГГ: ошибка: необходимо использовать косую черту, а не тире
  • Строка: 31.12.2010 Маска ДД / ММ / ГГГГ: ошибка: месяц должен быть вторым, а месяца нет 31 .
  • Строка: 12 / 31-11 Маска: ММ / ДД-ГГ: Пропуск: строка соответствует маске.

Прямо сейчас я использую index и substr, чтобы извлечь месяц, день и год, затем я использую xor, чтобы сгенерировать маску для всего остального. Это выглядит немного не элегантно, и мне было интересно, есть ли лучший способ сделать это:

my $self = shift;
my $date = shift;

my $format = $self->Format();

my $month;
my $year;
my $day;

my $monthIndex;
my $yearIndex;
my $dayIndex;

#
# Pull out Month, Day, and Year
#
if (($monthIndex = index($format, "MM")) != -1) {
    $month = substr($date, $monthIndex, 2);
}

if (($dayIndex = index($format, "DD")) != -1) {
    $day = substr($date, $dayIndex, 2);
}

if (($yearIndex = index($format, "YYYY")) != -1) {
    $year = substr($date, $yearIndex, 4);
}
elsif (($yearIndex = index($format, "YY")) != -1) {
    $year = substr($date, $yearIndex, 2);
    if ($year < 50) {
        $year += 2000;
    }
    else {
        $year += 1900;
    }
}

#
# Validate the Rest of Format
#

(my $restOfFormat = $format) =~ s/[MDY]/./g;    #Month Day and Year can be anything
if ($date !~ /^$restOfFormat$/) {
    return; #Does not match format
}
[...More Stuff before I return a true value...]

Я делаю это для даты, времени (используя ЧЧ , ММ , СС и А / * АА *) и IP-адреса в моем коде.


Кстати, я пытался использовать регулярные выражения, чтобы вытащить дату из строки, но это даже запутанно:

#-----------------------------------------------------------------------
# FIND MONTH
#
    my $mask = "M" x length($format);  #All M's the length of format string

    my $monthMask = ($format ^ $mask);      #Bytes w/ "M" will be "NULL"
    $monthMask =~ s/\x00/\xFF/g;    #Change Null bytes to "FF"
    $monthMask =~ s/[^\xFF]/\x00/g; #Null out other bytes

    #
    #   ####Mask created! Apply mask to Date String
    #

    $month = ($monthMask & $date);  #Nulls or Month Value
    $month =~ s/\x00//g;            #Remove Null bytes from string
#
#-----------------------------------------------------------------------

Это хитрый трюк в программировании, но было довольно сложно понять, что именно я делал, и, следовательно, было бы трудно поддерживать кого-то еще.

Ответы [ 3 ]

1 голос
/ 27 января 2011

Другой вариант может быть переписать ваш шаблон в шаблон strftime / strptime и протестировать с этими функциямиЯ использую версии, включенные в модуль core Time :: Piece .

use Time::Piece;

test('12/31/2010' => 'MM-DD-YYYY');
test('12/31/2010' => 'DD/MM/YYYY');
test('12/31-11'   => 'MM/DD-YY');

sub test {
    my ($time, $mask) = @_;
    my $t = eval { Time::Piece->strptime($time, make_format_from($mask)) };
    print "String: $time  Mask: $mask  "
      . (defined $t ? "Pass: ".$t->ymd : "Fail"), "\n";
}

sub make_format_from {
    my $mask = shift;
    for($mask) {
        s/YYYY/%Y/;
        s/YY/%y/;
        s/MM/%m/;
        s/DD/%d/;
    }
    return $mask;
}

Этот код дает

String: 12/31/2010  Mask: MM-DD-YYYY  Fail
String: 12/31/2010  Mask: DD/MM/YYYY  Fail
String: 12/31-11  Mask: MM/DD-YY  Pass: 2011-12-31
1 голос
/ 26 января 2011

Вы уже используете некоторые регулярные выражения в этом коде.Почему бы не преобразовать маску пользователя в шаблон и использовать регулярное выражение для прямой проверки ввода?Скажем,

$mask =~ s/YYYY/\\d{4}/;
# or:  $mask =~ s/YYYY/[12][0-9]{3}/
$mask =~ s/MM/(0[1-9]|1[0-2])/;               # MM => 01 - 12
$mask =~ s/DD/(0[1-9]|[12][0-9]|3[01])/;      # DD => 01 - 31
$mask =~ s/YY/\\d{2}/;                        # YY => 00 - 99
$mask = '^' . $mask . '$';

Так, например, это скомпилирует маску пользователя MM-DD-YY в шаблон ^(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])-\d{2}$, который можно протестировать с помощью:

if ($input =~ qr/$mask/) {
    print "Input is valid\n";
} else {
    print "Input is invalid\n";
}
0 голосов
/ 26 января 2011

Вы можете упростить использование регулярных выражений:

Для MM/DD-YY:

die "Wrong format" unless $date =~ /([01][0-9])\/([0-3][0-9])-([0-9][0-9])/;

Если это совпадает, скобки охватывают различные части и могут упоминаться как $1, $2 и т. Д. Затем используйте эти переменные для дальнейшего тестирования, например, если месяц находится между [1,12].

Кстати, этот шаблон не совместим с y2k ...

...