Python 2和3中're.sub'函数的不一致性问题

13

我正在编写一个在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个回答

10
我认为问题可能在于正则表达式模式匹配了子模式EN_EXTRACT_REGEXNUM_EXTRACT_REGEX中的一个,而不是两个都匹配。
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使用了不同版本的正则表达式引擎,这就是差异的原因。不幸的是,我无法确定差异的确切原因,但是在版本3.5中,re.sub()有一个记录的变化,涉及未匹配的组:

版本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
 abcd
 a 1 b 2 c 3
 aa__ bb__ 1122 cdef
_**_

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接