为什么 \0 在 Python 正则表达式替换中不起作用,即使用 sub() 或 expand(),而 match.group(0) 和 \1、\2、...?

Why doesn't \0 work in Python regexp substitutions, i.e. with sub() or expand(), while match.group(0) does, and also \1, \2, ...?

为什么 [=14=] 在 Python 正则表达式替换中不起作用(即 return 完全匹配),即 sub()match.expand(),而 match.group(0) 会,还有 </code>、<code>、... ?

这个简单的例子(在 Python 3.7 中执行)说明了一切:

import re

subject = '123'
regexp_pattern = r'\d(2)\d'
expand_template_full = r'[=12=]'
expand_template_group = r''

regexp_obj = re.compile(regexp_pattern)

match = regexp_obj.search(subject)
if match:
    print('Full match, by method: {}'.format(match.group(0)))
    print('Full match, by template: {}'.format(match.expand(expand_template_full)))
    print('Capture group 1, by method: {}'.format(match.group(1)))
    print('Capture group 1, by template: {}'.format(match.expand(expand_template_group)))

这个输出是:

Full match, by method: 123
Full match, by template: 
Capture group 1, by method: 2
Capture group 1, by template: 2

我可以在 replacement/expansion 模板中使用任何其他序列来获得完整匹配吗?如果不是,看在上帝的份上,为什么?

这是 Python 错误吗?

无论哪种方式都非常烦人。 :-(

如果你仔细研究 docs,你会发现下一个:

The backreference \g<0> substitutes in the entire substring matched by the RE.

更深入 docs回到 2003 年)你会发现下一个提示:

There is a group 0, which is the entire matched pattern, but it can't be referenced with [=12=]; instead, use \g<0>.

因此,您需要遵循此建议并使用 \g<0>:

expand_template_full = r'\g<0>'

呵呵,你说得对,真烦人!

幸运的是,Python领先于您。 sub 的文档是这样说的:

In string-type repl arguments, in addition to the character escapes and backreferences described above, \g<name> will use the substring matched by the group named name, as defined by the (?P<name>...) syntax. \g<number> uses the corresponding group number.... The backreference \g<0> substitutes in the entire substring matched by the RE.

因此您的代码示例可以是:

import re

subject = '123'
regexp_pattern = r'\d(2)\d'
expand_template_full = r'\g<0>'

regexp_obj = re.compile(regexp_pattern)

match = regexp_obj.search(subject)
if match:
    print('Full match, by template: {}'.format(match.expand(expand_template_full)))

您还提出了 "why?" 这个更有趣的问题。文档中的基本原理解释说您可以使用它来替换超过 10 个捕获组,因为不清楚 </code> 是否应该替换为第 10 组,或者第一个捕获组后跟一个零,但是没有解释为什么 <code>[=17=] 不起作用。我一直没能找到 PEP 来解释基本原理,但这是我的猜测:

我们希望 re.sub 的 repl 参数使用与正则表达式匹配中相同的捕获组反向引用语法。正则匹配时,[=17=] "backreferencing" 对整个匹配字符串的概念是无意义的;假设的正则表达式 r'A[=20=]' 将匹配无限长的 A 字符字符串,仅此而已。所以我们不能允许 [=17=] 作为反向引用存在。如果你不能匹配看起来像那样的反向引用,你也不应该用它来替换。

我不能说我同意这个逻辑,\g<> 已经是一个任意的扩展,但这是一个我可以看到有人提出的论点。

引用自https://docs.python.org/3/library/re.html

\number

Matches the contents of the group of the same number. Groups are numbered starting from 1. For example, (.+) matches 'the the' or '55 55', but not 'thethe' (note the space after the group). This special sequence can only be used to match one of the first 99 groups. If the first digit of number is 0, or number is 3 octal digits long, it will not be interpreted as a group match, but as the character with octal value number. Inside the '[' and ']' of a character class, all numeric escapes are treated as characters.

总结一下:

  • 使用 </code>、<code> 最多 </code>,前提是在编号的反向引用 </li> 之后没有更多数字 <li>使用<code>\g<0>\g<1>等(不限于99)来稳健地向后引用一个组
    • 据我所知,\g<0> 在替换部分很有用,可以引用整个匹配部分,但在搜索部分没有意义
    • 如果您使用第 3 方 regex 模块,那么 (?0) 在搜索部分也很有用,例如创建递归匹配模式