【问题标题】:How do I match for only one instance of a word in a string?如何仅匹配字符串中单词的一个实例?
【发布时间】:2018-05-12 13:00:00
【问题描述】:

如何只匹配字符串中某个单词的一个实例? 例如,以“fred”作为我的测试词,字符串“fredwilmawilliamjohn”的匹配应该为 TRUE,但“fredwilmawilliamfred”的匹配应该为 FALSE?

【问题讨论】:

  • 如果你在做Learning Perl中的练习,你应该自己做。

标签: regex perl match


【解决方案1】:

您可以通过在列表上下文中使用/g 标志来计算匹配数,并将其转换为标量上下文。

use strict;
use warnings;
use feature 'say';

for my $string (qw(fredwilmawilliamjohn fredwilmawilliamfred) ) {
    my $count = () = $string =~ m/(fred)/g;
    if ( $count  == 1 ) {
        say $string;
    }
}

最相关的部分是= () =,它强制匹配到列表上下文。然后my $count 会将该匹配列表强制转换为标量上下文,从而为您提供列表中的项目数。这是你的匹配数。

【讨论】:

  • 可以用纯正则表达式完成吗? $_="弗雷德威廉威尔玛"; if(/regexp/) {print "字符串中只有一个 fred"}
  • @SKS 你的意思是你想把所有的逻辑都放到模式中?我猜,弗雷德和负数一无所获或任何事情都跟着弗雷德。现在没有电脑。你必须自己尝试。
  • = () = 也被称为 'goatse operator' :)
【解决方案2】:
/fred(*COMMIT)(?!.*?fred)/s

(*COMMIT)(在 perl 5.10 中添加)表示在匹配 fred 之后,如果正则表达式的其余部分失败,请不要回溯并再次尝试匹配它。正则表达式的其余部分表示如果.*?fred(任意数量的任何字符后跟fred)在第一个fred之后匹配,则不匹配,即没有进一步的fred。

在 5.10 之前,您需要强制正则表达式中的第一个 fred 只匹配字符串中的第一个 fred,如下所示:

/^(?:(?!fred).)*+fred(?!.*?fred)/s

(匹配任意数量的字符,只要它们不是 fred 的开头,然后再匹配不能被另一个 fred 跟随的 fred)。

或者

/^(?>.*?fred)(?!.*?fred)/s

(从字符串的开头开始,不允许回溯,找到第一个fred,并断言后面没有fred)

(在整个过程中,/s 用于使. 匹配任何字符,而不仅仅是任何非换行符。)

【讨论】:

    【解决方案3】:

    听起来您想匹配该模式并验证它随后不会出现在字符串中。这个怎么样,使用负前瞻

    m/fred(?!.*fred)/
    

    分解:

    ?! -- 后面没有

    .* -- 零个或多个字符

    【讨论】:

    • 这行不通,因为正则表达式将尝试使负前瞻成功。如果它可以在字符串中找到与第二个 fred 不匹配的任何内容,则正则表达式成功。 !m/fred(?=.*fred)/ 可以在这里工作。
    • @ChrisCharley 原则上是正确的,但您说的并不完全正确。此正则表达式将阻止它匹配第一个 fred,但正则表达式的开头将在字符串中进一步尝试并匹配第二个 fred(后面没有 fred)
    • @ysth 感谢您澄清我的错误。我现在确切地看到了正则表达式的功能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    相关资源
    最近更新 更多