Perl正则表达式查找精确单词

3
我想在我的代码中查找单词 sprintf。应该使用哪个 Perl 正则表达式? 有些行包含类似 sprintf_private 的文本,我想将其排除,但只需要sprintf
2个回答

21

你必须在单词边界使用\b

/\bsprintf\b/

7

如果您想查找所有不包含 sprintf_private 的行中 sprintf 的所有出现,可以使用一对正则表达式:

while( my $line = <DATA> ) {
    next if $line =~ m/\bsprintf_private\b/;
    while( $line =~ m/\bsprintf\b/g ) {
        print "[sprintf] found on line $. at column $-[0]\n";
    }
}

首先,该程序会拒绝包含sprintf_private的所有行。然后扫描不包含该限定词的所有行,查找所有出现的sprintf。无论在何处找到它,在文件中标识该行和匹配起始列的消息都将被打印出来。

$.@-是在perlvar中描述的特殊变量。关于正则表达式的一些好文章可以在perlrequickperlretut中找到。第一个正则表达式非常简单;它只使用了\b零宽度断言来确保限定子字符串两边都有单词边界。第二个正则表达式使用了同样的技术,但还应用了/g修饰符以迭代处理所有出现的sprintf,以防万一每行可能会有多个出现。

零宽度断言\b匹配任何\w\W\W\w转换发生的位置。由于字符类\w包含所有字母字符(其中构成“所有”的内容取决于您的unicode_strings标志或/u),以及下划线和数字(即在标识符中允许使用的任何字符),您可能会发现\b单词边界过于严格。如果您发现“简单”解决方案过于简单,可以通过使用以下类似于这样的正则表达式来真正缩小应该作为单词边界限定的范围:

(?<!\p{Alpha})sprintf(?!\p{Alpha})

如果您选择这种方式,解决方案将如下所示:
while( my $line = <DATA> ) {
    next if $line =~ m/(?<!\p{Alpha})sprintf_private(?!\p{Alpha})/;
    while( $line =~ m/(?<!\p{Alpha})sprintf(?!\p{Alpha})/g ) {
        print "[sprintf] found on line $. at column $-[0]\n";
    }
}

这个使用了零宽度负向回顾和零宽度负向预查断言来拒绝匹配主要子串左侧或右侧紧挨着的字符是“Alpha”字符,而不是像稍微简单一点的\b

4
我很想知道“此回答无用”的具体原因,以便我可以改进它或者证明删除它是有道理的。请告诉我您的看法。 - DavidO
1
你的解释非常好,我给你点赞。我认为你可能被踩是因为问题没有说“在不包含snprint_private的行中查找所有sprintf出现的情况”,所以这可能被认为有点过度了。 - simbabque
谢谢。是的,我有一点担心规范。"在我的代码中查找sprintf这个单词 "似乎隐含了“所有实例”,而“一些包含类似sprintf_private文本的行,我想要排除掉”虽然更加模糊,但在我看来,它是说那些行上出现sprintf是可以忽略的。无论如何,我想技术上没有任何错误,所以我暂时保持原样。 - DavidO
我注意到原始问题被编辑,使snprint_private变成了sprintf_private,因此我已经更新了答案以反映这一变化。 - DavidO
我还看到有人可能会解释sprintf_private子句的意思是,虽然“sprintf”很重要,但如果后面跟着“_private”,那么它就不重要了。在这种情况下,“next if ...”第一个检查变得不必要,第二个检查应该以(?!\p{Alpha})结尾,这样下划线就被视为转换。 - DavidO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接