我试图在Perl中动态捕获正则表达式匹配。我知道eval会帮助我做到这一点,但我可能做错了。
代码:
use strict;
use warnings;
my %testHash = (
'(\d+)\/(\d+)\/(\d+)' => '$1$2$3'
);
my $str = '1/12/2016';
foreach my $pattern (keys (%testHash)) {
my $value = $testHash{$pattern};
my $result;
eval {
local $_ = $str;
/$pattern/;
print "\$1 - $1\n";
print "\$2 - $2\n";
print "\$3 - $3\n";
eval { print "$value\n"; }
}
}
是否也可以将捕获的正则表达式模式存储在数组中?
答案 0 :(得分:3)
我相信你真正想要的是以下动态版本:
say $str =~ s/(\d+)\/(\d+)\/(\d+)/$1$2$3/gr;
here提供了实现这一目标所需的条件。
use String::Substitution qw( gsub_copy );
for my $pattern (keys(%testHash)) {
my $replacement = $testHash{$pattern};
say gsub_copy($str, $pattern, $replacement);
}
请注意,$replacement
也可以是回调。这允许更复杂的替换。例如,如果您想将1/12/2016
转换为2016-01-12
,则可以使用以下内容:
'(\d+)/(\d+)/(\d+)' => sub { sprintf "%d-%02d-%02d", @_[3,1,2] },
回答你的实际问题:
use String::Substitution qw( interpolate_match_vars last_match_vars );
for my $pattern (keys(%testHash)) {
my $template = $testHash{$pattern};
$str =~ $pattern # Or /$pattern/ if you prefer
or die("No match!\n");
say interpolate_match_vars($template, last_match_vars());
}
答案 1 :(得分:3)
我不完全确定你想在这里做什么,但我认为你的计划不会像你认为的那样。
您正在使用eval
代码块。这就像try
块一样。如果它die
块内eval
,它将捕获该错误。它不会像代码一样运行你的字符串。你需要一个字符串eval
。
不是解释,而是替代方案。
此程序使用sprintf
and numbers the parameters。模式中的%1$s
语法表示_take第一个参数(1$
)并将其格式化为字符串(%s
)。您不需要本地化或分配给$_
来进行匹配。 =~
运算符会为您执行其他变量。我还使用qr{}
来创建一个引用的正则表达式(本质上是一个包含预编译模式的变量),我可以直接使用它。由于{}
作为分隔符,我不需要逃避斜杠。
use strict;
use warnings;
use feature 'say'; # like print ..., "\n"
my %testHash = (
qr{(\d+)/(\d+)/(\d+)} => '%1$s.%2$s.%3$s',
qr{(\d+)/(\d+)/(\d+) nomatch} => '%1$s.%2$s.%3$s',
qr{(\d+)/(\d+)/(\d\d\d\d)} => '%3$4d-%2$02d-%1$02d',
qr{\d} => '%s', # no capture group
);
my $str = '1/12/2016';
foreach my $pattern ( keys %testHash ) {
my @captures = ( $str =~ $pattern );
say "pattern: $pattern";
if ($#+ == 0) {
say " no capture groups";
next;
}
unless (@captures) {
say " no match";
next;
}
# debug-output
for my $i ( 1 .. $#- ) {
say sprintf " \$%d - %s", $i, $captures[ $i - 1 ];
}
say sprintf $testHash{$pattern}, @captures;
}
我包括四个例子:
%1$s
等等。@captured
中的元素数量。sprintf
格式。$#
作为通常具有@
sigil)in @+
的数组的sigil,其中保存了结尾的偏移量当前活动动态范围中最后成功的子匹配。第一个元素是整体匹配的结束,所以如果这只有一个元素,我们就没有捕获组。我的输出是:
pattern: (?^:(\d+)/(\d+)/(\d\d\d\d))
$1 - 1
$2 - 12
$3 - 2016
2016-12-01
pattern: (?^:(\d+)/(\d+)/(\d+) nomatch)
no match
pattern: (?^:\d)
no capture groups
pattern: (?^:(\d+)/(\d+)/(\d+))
$1 - 1
$2 - 12
$3 - 2016
1.12.2016
请注意,输出中的顺序是混合的。这是因为哈希没有在Perl中排序,如果你在没有sort
的哈希中迭代键,则顺序是随机的。
答案 2 :(得分:0)
道歉!我意识到我的问题和示例代码都含糊不清。但在阅读了你的建议后,我得到了以下代码。 我还没有对此代码进行优化,并且替换存在限制。
foreach my $key (keys %testHash) {
if ( $str =~ $key ) {
my @matchArr = ($str =~ $key); # Capture all matches
# Search and replace (limited from $1 to $9)
for ( my $i = 0; $i < @matchArr; $i++ ) {
my $num = $i+1;
$testHash{$key} =~ s/\$$num/$matchArr[$i]/;
}
$result = $testHash{$key};
last;
}
}
print "$result\n";
答案 3 :(得分:-1)
是否也可以将捕获的正则表达式模式存储在数组中?
当然可以将捕获的子串存储在一个数组中:
#!/usr/bin/env perl
use strict;
use warnings;
my @patterns = map qr{$_}, qw{
(\d+)/(\d+)/(\d+)
};
my $str = '1/12/2016';
foreach my $pattern ( @patterns ) {
my @captured = ($str =~ $pattern)
or next;
print "'$_'\n" for @captured;
}
输出:
'1' '12' '2016'
我不太明白您在代码中使用local
,eval EXPR
和eval BLOCK
的组合以及以下哈希的目的要做什么:
my %testHash = ( '(\d+)\/(\d+)\/(\d+)' => '$1$2$3' );
如果你试图编纂这个模式应该导致三次捕获,你可以这样做:
my @tests = (
{
pattern => qr{(\d+)/(\d+)/(\d+)},
ncaptures => 3,
}
);
my $str = '1/12/2016';
foreach my $test ( @tests ) {
my @captured = ($str =~ $test->{pattern})
or next;
unless (@captured == $test->{ncaptures}) {
# handle failure
}
}
请参阅states,了解如何自动计算模式中捕获组的数量。在答案中使用该技术:
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
my @tests = map +{ pattern => qr{$_}, ncaptures => number_of_capturing_groups($_) }, qw(
(\d+)/(\d+)/(\d+)
);
my $str = '1/12/2016';
foreach my $test ( @tests ) {
my @captured = ($str =~ $test->{pattern});
ok @captured == $test->{ncaptures};
}
done_testing;
sub number_of_capturing_groups {
"" =~ /|$_[0]/;
return $#+;
}
输出:
ok 1 1..1
答案 4 :(得分:-1)
评估列表上下文中的正则表达式返回匹配项。所以在你的例子中:
use Data::Dumper; # so we can see the result
foreach my $pattern (keys (%testHash)) {
my @a = ($str =~/$pattern/);
print Dumper(\@a);
}
可以胜任。
HTH 乔治