我有一个程序允许用户指定一个掩码,如MM-DD-YYYY,并将其与字符串进行比较。在字符串中, MM 将被假定为一个月, DD 将是该月的日期,YYYY将是年份。其他一切必须完全匹配:
现在,我使用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...]
我正在做这个日期,时间(使用 HH , MM , SS 和 A / * AA *),以及我的代码中的IP地址。
BTW,我尝试使用正则表达式从字符串中提取日期,但它甚至更加混乱:
#-----------------------------------------------------------------------
# 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
#
#-----------------------------------------------------------------------
这是一个简洁的编程技巧,但很难准确理解我在做什么,从而使其他人难以维护。
答案 0 :(得分:1)
您已在此代码中使用了一些正则表达式。为什么不将用户的掩码转换为模式并使用正则表达式直接验证输入?说,
$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";
}
答案 1 :(得分:1)
另一种选择可能是将您的模式重写为strftime/strptime pattern并使用这些功能进行测试。我正在使用核心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
答案 2 :(得分:0)
您可以使用正则表达式进行简化:
MM/DD-YY
:
die "Wrong format" unless $date =~ /([01][0-9])\/([0-3][0-9])-([0-9][0-9])/;
如果匹配,则括号捕获不同的部分,并且可以称为$1
,$2
等。然后使用这些变量进行进一步测试,例如:如果月份在[1,12]之间。
顺便说一句,这种模式不兼容y2k ......