Python 2 和 3 're.sub' 不一致

Posted

技术标签:

【中文标题】Python 2 和 3 \'re.sub\' 不一致【英文标题】:Python 2 and 3 're.sub' inconsistencyPython 2 和 3 're.sub' 不一致 【发布时间】:2018-01-23 07:36:26 【问题描述】:

我正在编写一个函数来从 python 中的文本中拆分数字和其他一些东西。代码如下所示:

EN_EXTRACT_REGEX = '([a-zA-Z]+)'
NUM_EXTRACT_REGEX = '([0-9]+)'
AGGR_REGEX = EN_EXTRACT_REGEX + '|' + NUM_EXTRACT_REGEX

entry = re.sub(AGGR_REGEX, r' \1\2', entry)

现在,此代码在 python3 中运行良好,但在 python2 下无法运行并出现“unmatched group”错误。

问题是,我需要同时支持这两个版本,虽然我尝试了各种其他方式,但我无法让它在 python2 中正常工作。

我很好奇这个问题的根源是什么,有什么解决方法吗?

【问题讨论】:

【参考方案1】:

我认为问题可能在于正则表达式模式匹配一​​个子模式EN_EXTRACT_REGEXNUM_EXTRACT_REGEX 中的另一个,但不能同时匹配两者。 p>

re.sub() 匹配第一个模式中的字母字符时,它会尝试用\2 替换第二组引用,但失败了,因为只有第一组匹配 - 没有第二组。

同样,当数字模式匹配时,没有 \1 组可以替换,因此这也会失败。

您可以看到在 Python 2 中的这个测试就是这种情况:

>>> re.sub(AGGR_REGEX, r' \1', 'abcd')    # reference first pattern
 abcd
>>> re.sub(AGGR_REGEX, r' \2', 'abcd')    # reference second pattern
Traceback (most recent call last):
....
sre_constants.error: unmatched group

差异必须存在于 Python 2 和 Python 3 的正则表达式引擎的不同版本中。不幸的是,我无法提供差异的明确原因,但是,re.sub() 的版本 3.5 中有一个记录在案的更改关于不匹配组:

3.5 版更改:不匹配的组被替换为空字符串。

这解释了为什么它在 Python >= 3.5 中有效,但在早期版本中无效:不匹配的组基本上会被忽略。


作为一种解决方法,您可以更改模式以将两个匹配项作为一个组处理:

import re

EN_EXTRACT_REGEX = '[a-zA-Z]+'
NUM_EXTRACT_REGEX = '[0-9]+'
AGGR_REGEX = '(' + EN_EXTRACT_REGEX + '|' + NUM_EXTRACT_REGEX + ')'
# ([a-zA-Z]+|[0-9]+)

for s in '', '1234', 'abcd', 'a1b2c3', 'aa__bb__1122cdef', '_**_':
    print(re.sub(AGGR_REGEX, r' \1', s))

输出

1234 A B C D a 1 b 2 c 3 aa__ bb__ 1122 cdef _**_

【讨论】:

以上是关于Python 2 和 3 're.sub' 不一致的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 3.9 中从 re.sub 中删除反斜杠

在Python中for循环和re.sub怎么一起用

为啥 re.sub() 在 Python 中默认添加不匹配的字符串?

Python re.sub 替换 html 属性

python re 与 re.sub替换部分文件

Python排序和删除列表中的重复项使用re.sub