为什么Python中的re.sub在这个测试案例上不正确?
试试这个代码。
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