【问题标题】:What's the best way to clear regex matching variables?清除正则表达式匹配变量的最佳方法是什么?
【发布时间】:2012-04-18 20:35:09
【问题描述】:

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

  • 示例$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)之后,阅读多行代码会更加简洁,我会发现这种语法更快地理解。

【问题讨论】:

  • 这个问题也是“如果我要自己开枪,我应该瞄准什么?”
  • @briandfoy: 正是:) - 看到了 oscon 的日程安排,期待看到你的新闻。再次。我认为人们在想我不明白if (//) 的作用以及为什么要使用它。通常,当我在这里时,是因为其他人没有,而我正在寻找最简单的方法来摆脱他们的代码。在这种情况下,我的答案是唯一回答我提出的问题的答案。 更正:我的和 ikegami.
  • 那你为什么不这样问呢?对情况的解释有时会使愚蠢的问题变成好问题。
  • @mugenkenichi:我想我时间紧迫,但是你可能是对的 ;)
  • 你仍然可以编辑你的问题来解释你为什么问它。但是,如果您时间紧迫,您为什么要发布一个推测性的问题呢?除了浪费别人的时间,你不应该做其他事情吗?您似乎有足够的时间来评论其他人的答案。

标签: regex perl backreference


【解决方案1】:

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

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

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

【讨论】:

  • 更重要的一点是,您不应该做任何依赖于重置反向引用的事情。这是一个如何避免对您发布的代码的依赖的示例;如果您有不同的示例,请发布它,以便我们演示如何在那里避免它。在成功匹配之后以外的任何上下文中依赖 $1 的内容是一个错误。期间。
  • @vol7ron 不。这完全是现场和记录的行为。 perlvar
  • perlre 明确指出“Perl 中失败的匹配不会重置匹配变量,这样可以更轻松地编写测试一系列更具体情况并记住最佳匹配的代码。”跨度>
  • @vol7ron:很公平。在您的情况下,我仍然会寻找其他不涉及这种黑客攻击的相对低影响的重构机会,但是如果您决心走那条路……我不认为存在范围差异,其中如果您可以将 $1 声明为本地?如果做不到这一点,我认为您自己的解决方案是唯一的选择。
  • ...或者您可能根本无法使用 $1 等,而是将匹配结果分配给词法变量。如果你做my @a = /.../,那么如果匹配失败,$a[1] 将是 undef。
【解决方案2】:

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

"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>。您实际上是在谈论“正则表达式捕获缓冲区”。

【讨论】:

  • 如果可以的话,我会给你加倍的分数来纠正我。不过,文档称其为“匹配变量”;)
  • 我看到“匹配变量”的唯一地方是perlrequickperlretut
  • @brian d foy,我在 perlre 中看到了“匹配变量”(这比“匹配变量”更有意义),但我真的不喜欢这个词。如果不是毫无意义的话,它就是模棱两可的。 “捕获缓冲区”在文档中被显着使用(至少在 perlre 和 perlvar 中)。 $1 在技术上只是“允许访问捕获缓冲区”而不是捕获缓冲区本身,但那是头发分裂。
  • 不是所有的匹配变量都是捕获缓冲区。
  • @brian d foy,什么意思?文档说\1k<name> 访问捕获缓冲区,因此记录了命名和编号的捕获以访问捕获缓冲区。那会留下什么?
【解决方案3】:

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

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

【讨论】:

  • 你没有抓住重点。这是一个示例(仅部分代码)。问题是关于重置反向引用。
  • @vol7ron 你没抓住重点。当全局变量无效时停止读取它们的值。
  • @hobbs: :) 没有。在所有人中,你应该知道,当代码变得更复杂时,你应该以不同的方式构造它以使其更易于理解。你有时会做if () {...} vs do {...} if ()的全部原因
  • @vol7ron 当代码变得更复杂时,您会尽一切可能避免远距离操作,例如使用过去某个时间可能设置或未设置的$1 .
  • 只有在不破坏代码的情况下,您才能重组代码以使其更具可读性。当您不只是成功匹配时,依赖 $1 的值会破坏代码。您应该将此视为一个信号,无论您尝试做什么,都需要以不同的方式进行。
【解决方案4】:

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

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

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

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

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

$_="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'
  • 这种方法的优点是没有当前作用域的特殊正则表达式变量被设置或更改;相比之下,接受的答案会改变变量,例如 $&$'
  • 缺点是必须明确传递感兴趣的变量;但是,默认情况下,您确实会得到匹配操作的结果,如果您只对捕获缓冲区的内容感兴趣,那就足够了。

【讨论】:

    【解决方案5】:

    你应该这样做:

    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;
    

    【讨论】:

    • 那很脏……因为有更多的意识形态方法来处理这个问题。
    • $& 应该不惜一切代价避免。在您的示例中,$1 将具有完全相同的数据并避免全局性能损失。
    • @Ven'Tatsu - 当然我们都知道$&是最后一场比赛,所以在这种情况下是$1,但你能解释一下你在说什么性能惩罚吗?跨度>
    • 阅读$& 上的perlvar 条目,了解众所周知和通常可以避免的性能损失。
    • @briandfoy - 谢谢你的解释,不知道。
    【解决方案6】:

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

    for ("match", "fail") {
        my ($fake_1) = /(m.+)/;
        doSomething($fake_1) if $fake_1;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-10-23
      • 2011-04-14
      • 1970-01-01
      • 1970-01-01
      • 2015-07-06
      • 1970-01-01
      • 2023-02-25
      • 2021-12-11
      • 1970-01-01
      相关资源
      最近更新 更多