如何使用正则表达式进行匹配,只要它不在开头?

66
假设字符串如下:
aaa bbb ccc
bbb aaa ccc

我想匹配的是aaa,但不要在字符串开头匹配。我试图通过这样做来否定它:

[^^]aaa

但我不认为这是正确的。使用preg_replace


你只匹配aaa吗?用什么替换它? - Explosion Pills
6个回答

117
您可以使用“向后查找”来确保它不在开头。(?<!^)aaa

有没有办法否定位置断言? - StackOverflowNewbie
1
我仍然需要捕获aaa。只需用括号括起来? - StackOverflowNewbie
5
对于 JavaScript,仅使用 (?!^)aaa。 (注:这是一条代码片段,意思是指在 JavaScript 中使用正则表达式,匹配所有包含 "aaa" 的字符串,但不匹配以 "aaa" 开头的字符串。) - Bernhard
1
[^\n]aaa 也可以完成任务(对我来说已经完成了),而且更简单,因此在没有可用的后顾断言时可以使用。 - polynomial_donut
2
如果您在R中使用gsub()进行此操作,只需设置perl = T选项即可。 - Brian D

46

由于我通过谷歌搜索来到这里,并且对不使用lookbehind的解决方案很感兴趣,这是我的建议。

[^^]aaa模式匹配一个字符,除了^之外,并且在字符串中的任何位置匹配3个as。 [^...]是一个否定字符类,其中^不被视为特殊字符。请注意,紧接在[后面的第一个^是特殊的,因为它表示否定,而第二个则只是字面上的插入符号。

因此,[...]内部不能有^表示字符串的开头。

解决方法是使用任何负面环视,以下两种方法都可以:

(?<!^)aaa

并且有一个预测:

(?!^)aaa

为什么前瞻也能起作用呢?前瞻是零宽度断言,而锚点也是零宽度的,它们不会消耗任何文本。简单来说,(?<!^)检查当前位置左侧是否没有紧接着字符串开头的位置,(?!^)则检查当前位置右侧是否没有紧接着字符串开头的位置。这两个表达式都是在检查同一些位置,所以两者都能正常工作。


1
感谢提供前瞻替代方案,因为Javascript尚不支持后顾。 - Simon Corcos
回溯支持已经在ECMAScript 2018+中引入,因此您可以在Node.JS和Chrome中使用(?<!^),但是在遵循ES5标准和旧的JS环境(包括IE)的VBA中仍然可以使用(?!^) - Wiktor Stribiżew
后行断言仅支持Chrome 62+和Opera 49+(https://caniuse.com/#feat=js-regexp-lookbehind)。 - GetFree

16

如果你不想使用回顾后发断言,那么可以使用这个正则表达式:

/.(aaa)/

并使用匹配组#1


1
这是最佳答案,因为它也适用于其他正则表达式方言。 - tmalsburg
3
除非你的正则表达式引擎支持重叠匹配,否则它不会匹配连续出现的aaa。例如,对于字符串xaaaaaa,只会有一个匹配。 - Black Mantha

5
这种情况是我第一次看到环视优于\K。有趣。
通常捕获组和环视需要额外的步骤。但由于这个任务的性质,正则表达式引擎可以更快地在字符串中搜索aaa,然后回溯查找字符串锚点的开头。
我会添加几个\K模式进行比较。
我使用了s模式修饰符,以防止前导字符可能是换行符(.通常无法匹配)。我只是想提前考虑到可能存在的特殊情况。
再次强调,这是一个启示性的场景,因为在我处理过的所有其他正则表达式案例中,\K都胜过了其他技术。
步数比较矩阵:
              | `~.\Kaaa~s` | `~.+?\Kaaa~s` | `(?<!^)aaa` | `(?!^)aaa` | `.(aaa)` |
--------------|-------------|---------------|-------------|------------|----------|
`aaa bbb ccc` |   12 steps  |    67 steps   |   8 steps   |  8 steps   | 16 steps |
--------------|-------------|---------------|-------------|------------|----------|
`bbb aaa ccc` |   15 steps  |    12 steps   |   6 steps   |  6 steps   | 12 steps |

重点是:要了解您的模式效率,将其分解成regex101.com,并比较步骤计数。
此外,如果您确切地知道要查找的子字符串并且不需要正则表达式模式,则应使用strpos()作为最佳实践(只需检查返回值是否> 0)
换句话说:
if (strpos($haystack, 'aaa')) {
    // 'aaa' is "truthy"
    // 'aaa' is found and not positioned at offset zero
}

2
在regex101.com上检查正则表达式模式效率的有用建议。 - Simon Elms

3

1
我来到这里寻找re2引擎的解决方案,该引擎被Google电子表格使用,但不支持环视。 但是这里的答案给了我以下的想法。 我不明白为什么我必须用捕获组替换,但不管怎样,它有效。

aaa bbb ccc
bbb aaa ccc

([^^])aaa

replace by:

$1zzz

结果为:

aaa bbb ccc
bbb zzz ccc


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