54

假设以下字符串:

aaa bbb ccc
bbb aaa ccc

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

[^^]aaa

但我认为这是不对的。使用preg_replace.

4

6 回答 6

91

您可以使用向后看来确保它不是在开头。(?<!^)aaa

于 2013-03-27T21:11:40.133 回答
38

因为我是通过谷歌搜索来到这里的,并且对不使用后视的解决方案感兴趣,所以这是我的 2 美分。

[^^]aaa模式匹配字符串中任何位置^的字符,然后是 3 as 。这是一个不被视为特殊字符的否定字符类。请注意,紧随其后的第一个是特殊的,因为它表示否定,第二个只是文字插入符号。[^...]^^[

因此,a^不能在内部[...]表示字符串的开始。

一个解决方案是使用任何负面的环视,这两个将同样有效:

(?<!^)aaa

和前瞻:

(?!^)aaa

为什么前瞻也有效?Lookarounds是零宽度的断言,而锚点也是零宽度的——它们不消耗任何文本。从字面上看,(?<!^)检查当前位置左侧是否没有字符串开始位置,并(?!^)检查当前位置右侧是否没有字符串开始位置。正在检查相同的位置,这就是为什么两者都运行良好的原因。

于 2017-04-05T13:28:31.293 回答
16

如果您不想使用lookbehind,请使用此正则表达式:

/.(aaa)/

并使用matched group # 1.

于 2013-03-27T21:20:06.503 回答
5

这种情况是我第一次看到lookarounds跑赢大盘\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
}
于 2018-04-23T08:36:37.927 回答
3

这将有助于找到您正在寻找的内容:

(?<!^)aaa

使用示例:http ://regexr.com?34ab2

于 2013-03-27T21:20:10.167 回答
1

我来这里是为了寻找 re2 引擎的解决方案,它被 google 电子表格使用,它不支持环视。但是这里的答案给了我使用以下内容的想法。我不明白为什么我必须用捕获的组替换,但无论如何,它有效。

aaa bbb ccc
bbb aaa ccc

([^^])aaa

替换为:

$1zzz

结果:

aaa bbb ccc
bbb zzz ccc

于 2019-01-23T16:14:15.013 回答