为什么Python中的re.sub在这个测试案例上不正确?

4 投票
4 回答
2615 浏览
提问于 2025-04-16 07:42

试试这个代码。

test = ' az z bz z z stuff z  z '
re.sub(r'(\W)(z)(\W)', r'\1_\2\3', test)

这个代码应该能把所有单独的 z 替换成 _z。

但是,结果是:

' az _z bz _z z stuff _z _z '

你会发现有一个 z 没被替换掉。我推测这是因为在匹配两个 z 的时候,分组无法抓住 z 之间的空格(一个是后面的空格,一个是前面的空格)。有没有办法解决这个问题呢?

4 个回答

5

你想要避免捕捉到空格。可以试试使用零宽度的单词边界 \b,像这样:

re.sub(r'\bz\b', '_z', test)
7

如果你的目标是确保只在z是一个独立的单词时进行匹配,可以使用\b来匹配单词的边界,而不实际消耗空格:

>>> re.sub(r'\b(z)\b', r'_\1', test)
' az _z bz _z _z stuff _z  _z '
4

之所以会出现这种情况,是因为你得到了一个重叠的匹配;你需要避免匹配到多余的字符。你可以通过两种方式来解决这个问题:一种是使用 \b,也就是单词边界,正如其他人所建议的那样;另一种是使用向后查找断言向前查找断言。(如果可以的话,应该优先使用 \b,这个方法主要是为了教学目的。)

>>> re.sub(r'(?<!\w)(z)(?!\w)', r'_\1', test)
' az _z bz _z _z stuff _z  _z '

(?<!\w) 确保前面没有 \w 字符。

(?!\w) 确保后面没有 \w 字符。

特殊的 (?...) 语法表示它们不是分组,所以 (z)\1


关于为什么会失败的图形解释:

这个正则表达式正在遍历字符串进行替换;它在这三个字符上:

' az _z bz z z stuff z  z '
          ^^^

它进行替换。最后一个字符已经被处理,所以它的下一步大致是这样的:

' az _z bz _z z stuff z  z '
              ^^^ <- It starts matching here.
             ^ <- Not this character, it's been consumed by the last match

撰写回答