Python正则中获取前一个分组

5 投票
3 回答
704 浏览
提问于 2025-04-17 22:00

我想捕捉一些看起来像 %a%b 这样的字符串片段,并把它们替换成一些值。此外,我还想通过输入 %% 来转义 % 字符。

在一个示例字符串 %d%%f%x%%%g 中,我想匹配到 %d%%f%x%%%g(也就是 %d%x%g)。

我的正则表达式是这样的:

(?:[^%]|^)(?:%%)*(%[a-z])
  • (?:[^%]|^) - 匹配行的开头或者不是 % 的字符
  • (?:%%)* - 匹配 0 次或多次的 %%(转义的 %
  • (%[a-z]) - 正确匹配 %a%b 等模式

前两个部分是为了支持转义 % 字符。

但是,当我在示例字符串上运行这个正则表达式时,最后一个片段(%g)没有被找到:

>>> import re
>>> pat = re.compile("(?:[^%]|^)(?:%%)*(%[a-z])")
>>> pat.findall("%d%%f%x%%%g")
['%d', '%x']

不过在 %%%g 前面加一个字符后,它就开始正常工作了:

>>> pat.findall("%d%%f%x %%%g")
['%d', '%x', '%g']

看起来在匹配到 (%[a-z]) 组后,x 没有再与 [^%] 匹配。我该如何修改正则表达式,让它强制检查上一个匹配的最后一个字符呢?我读过 \G,但没有帮助。

3 个回答

2

看起来你想要找到每个前面有偶数个%的部分%x

如果是这样的话,使用的模式是"(?<!%)(?:%%)*(%[a-z])"

2

你需要稍微调整一下你的正则表达式构造方式:

>>> import re
>>> regex = re.compile(r"(?:[^%]|%%)*(%[a-z])")
>>> regex.findall("%d%%f%x%%%g")
['%d', '%x', '%g']

解释:

(?:      # Start of a non-capturing group:
 [^%]    # Either match any character except %
|        # or
 %%      # match an "escaped" %.
)*       # Do this any number of times.
(        # Match and capture in group 1:
 %[a-z]  # % followed by a lowercase ASCII alphanumeric
)        # End of capturing group
3

为什么没有选择 %g 呢?

要选择 %g,前面必须有 %%。而在这之前,必须有一个不是 % 的字符,或者它要在字符串的开头。所以,像 x%%%g 这样的字符串是可以匹配到的。但是,这里的 x 是在之前的匹配中选出来的(也就是当打印 %x 的时候)。

简单来说,你的正则表达式匹配出现了重叠。所以你可以用下面这个方法来解决这个问题。我把你的正则表达式放在了 (?= ... ) 里面。

pat = re.compile("(?=(?:[^%]|^)(?:%%)*(%[a-z]))")

撰写回答