6

清除/重置所有正则表达式匹配变量的最佳方法是什么?

  • 示例如何$1在正则表达式操作之间不重置并使用最近的匹配:

    $_="this is the man that made the new year rumble"; 
    / (is) /; 
    / (isnt) /; 
    say $1;          # outputs "is"
    
  • 示例在使用循环时这可能会出现问题:

    foreach (...){
       /($some_value)/;
       &doSomething($1) if $1;
    }
    

更新:我认为我不需要这样做,但 Example-2 只是一个示例。这个问题是关于重置匹配变量,而不是实现它们的最佳方法。

无论如何,最初我的编码风格更符合明确和使用 if 块。现在回到这个(Example2)之后,阅读多行代码会更加简洁,我会发现这种语法更快地理解。

4

6 回答 6

18

您应该使用匹配的返回值,而不是组变量的状态。

foreach (...) {
    doSomething($1) if /($some_value)/;
}

$1 等仅在匹配成功时才保证反映最近的匹配。除了在一场成功的比赛之后,你不应该看他们。

于 2012-04-18T20:43:08.947 回答
14

成功匹配会重置正则表达式捕获*。要重置正则表达式捕获,可以使用保证匹配的简单匹配操作。

"a" =~ /a/;  # Reset captures to undef.

是的,它看起来很奇怪,但你要求做一些奇怪的事情。

如果你修复了你的代码,你就不需要看起来很奇怪的解决方法。修复您的代码甚至会发现一个错误!

修复:

$_ = "this is the man that made the new year rumble"; 
if (/ (is) / || / (isnt) /) {
   say $1; 
} else{ 
   ...  # You're currently printing something random.
}

for (...) {
   if (/($some_pattern)/) {
      do_something($1);
   }
}

* — Backrefs 是匹配先前捕获的文本的正则表达式模式。例如\1\k<foo>。您实际上是在谈论“正则表达式捕获缓冲区”。

于 2012-04-18T21:54:44.357 回答
5

您应该测试匹配是否成功。例如:

foreach (...){
   /($some_value)/ or next;
   doSomething($1) if $1;
}

foreach (...){
   doSomething($1) if /($some_value)/ and $1;
}

foreach (...){
   if (/($some_value)/) {
      doSomething($1) if $1;
   }
}

根据是什么$some_value,以及您希望如何处理匹配空字符串和/或 0,您可能需要也可能根本不需要测试$1

于 2012-04-18T20:44:21.613 回答
2

为了补充现有的有用答案(以及在布尔上下文中正常测试匹配操作的结果并仅在测试成功时才采取行动的明智建议):

根据您的情况,您可以以不同的方式解决问题:

免责声明:我不是经验丰富的 Perl 程序员;如果这种方法有问题,请告诉我。

将匹配操作包含在do { ... }块中,将所有与正则表达式相关的特殊变量($&, $1, ...)都包含在该块中。

因此,您可以使用 ado { ... }来防止这些特殊变量首先被设置(尽管来自块外先前正则表达式操作的变量显然仍然有效);例如:

$_="this is the man that made the new year rumble"; 

# Match in current scope; -> $&, $1, ... *are* set.
/ (is) /;

# Match inside a `do` block; the *new* $&, $1, ... values
# are set only *inside* the block; 
# `&& $1` passes out the block's version of `$1`.
$do1 = do { / (made) / && $1 };

print "\$1 == '$1'; \$do1 == '$do1'\n";  # -> $1 == 'is'; $do1 == 'made'
  • 这种方法的优点是没有设置或更改当前作用域的特殊正则表达式变量;相比之下,接受的答案会改变变量,例如$&$'
  • 缺点是必须显式传递感兴趣的变量;但是,默认情况下,您确实会得到匹配操作的结果,如果您只对捕获缓冲区的内容感兴趣,那就足够了。
于 2015-07-17T18:05:37.940 回答
1

你应该这样做:

foreach (...) { 
   someFnc($1) if /.../; 
}

但是,如果您想坚持自己的风格,请将此作为一个想法:

$_ = "this is the man that made the new year rumble";  

$m = /(is)/   ? $1 : undef;
$m = /(isnt)/ ? $1 : undef;

print $m, "\n" if defined $m;
于 2012-04-18T20:55:33.370 回答
0

将捕获分配给列表的行为更接近您想要的声音。

for ("match", "fail") {
    my ($fake_1) = /(m.+)/;
    doSomething($fake_1) if $fake_1;
}
于 2012-04-18T21:53:07.920 回答