Python正则表达式模块与重新模块 - 模式不匹配

时间:2017-04-22 19:42:26

标签: python regex

更新:开发人员在 commit be893e9

中解决了此问题

如果遇到同样的问题,请更新regex模块。
您需要版本2017.04.23或更高版本。

正如 this answer 所指出的那样 我需要 this regular expression

(?i)\b((\w{1,3})(-|\.{2,10})[\t ]?)+(\2\w{2,})

同时使用regex模块......

import re     # standard library
import regex  # https://pypi.python.org/pypi/regex/

content = '"Erm....yes. T..T...Thank you for that."'
pattern = r"(?i)\b((\w{1,3})(-|\.{2,10})[\t ]?)+(\2\w{2,})"
substitute = r"\2-\4"

print(re.sub(pattern, substitute, content))
print(regex.sub(pattern, substitute, content))

输出:

"Erm....yes. T-Thank you for that."
"-yes. T..T...Thank you for that."

问:我如何编写此正则表达式以使regex模块以与re模块相同的方式对其作出反应?

使用re模块不是一个选项,因为我需要动态长度的后视镜。

澄清:如果正则表达式适用于两个模块会很好,但最后我只需要regex

2 个答案:

答案 0 :(得分:5)

这个错误似乎与回溯有关。它是在重复捕获组时发生的,并且捕获组匹配但之后的模式不匹配。

一个例子:

>>> regex.sub(r'(?:(\d{1,3})x)+', r'\1', '123x5')
'5'

作为参考,预期输出为:

>>> re.sub(r'(?:(\d{1,3})x)+', r'\1', '123x5')
'1235'

在第一次迭代中,捕获组(\d{1,3})消耗前3位数,x消耗以下“x”字符。然后,由于+,第二次尝试匹配。这一次,(\d{1,3})匹配“5”,但x无法匹配。但是,捕获组的值现在(重新)设置为空字符串而不是预期的123

作为一种解决方法,我们可以阻止捕获组进行匹配。在这种情况下,将其更改为(\d{2,3})足以绕过该错误(因为它不再匹配“5”):

>>> regex.sub(r'(?:(\d{2,3})x)+', r'\1', '123x5')
'1235'

对于有问题的模式,我们可以使用先行断言;我们将(\w{1,3})更改为(?=\w{1,3}(?:-|\.\.))(\w{1,3})

>>> pattern= r"(?i)\b((?=\w{1,3}(?:-|\.\.))(\w{1,3})(-|\.{2,10})[\t ]?)+(\2\w{2,})"
>>> regex.sub(pattern, substitute, content)
'"Erm....yes. T-Thank you for that."'

答案 1 :(得分:1)

修改bug现已在正则表达式中解析2017.04.23

刚刚在Python 3.6.1中测试过,原始模式在reregex

中的作用相同

原始解决方法 - 您可以使用惰性运算符+?(即在T...Tha....Thank等边缘情况下,其行为与原始模式不同的正则表达式):

pattern = r"(?i)\b((\w{1,3})(-|\.{2,10})[\t ]?)+?(\2\w{2,})"

<小时/> 2017.04.05中的错误是由于回溯,如下所示:

不成功的较长匹配会创建空的\2组,从概念上讲,它应该触发回溯到较短的匹配,其中嵌套组不为空,但regex似乎&#34;优化&#34 ;并且不会从头开始计算较短的匹配,但会使用一些缓存的值,忘记撤消嵌套匹配组的更新。

示例贪婪匹配((\w{1,3})(\.{2,10})){1,3}将首先尝试3次重复,然后回溯到更少:

import re
import regex

content = '"Erm....yes. T..T...Thank you for that."'
base_pattern_template = r'((\w{1,3})(\.{2,10})){%s}'
test_cases = ['1,3', '3', '2', '1']

for tc in test_cases:
    pattern = base_pattern_template % tc
    expected = re.findall(pattern, content)
    actual = regex.findall(pattern, content)
    # TODO: convert to test case, e.g. in pytest
    # assert str(expected) == str(actual), '{}\nexpected: {}\nactual: {}'.format(tc, expected, actual)
    print('expected:', tc, expected)
    print('actual:  ', tc, actual)

输出:

expected: 1,3 [('Erm....', 'Erm', '....'), ('T...', 'T', '...')]
actual:   1,3 [('Erm....', '', '....'), ('T...', '', '...')]
expected: 3 []
actual:   3 []
expected: 2 [('T...', 'T', '...')]
actual:   2 [('T...', 'T', '...')]
expected: 1 [('Erm....', 'Erm', '....'), ('T..', 'T', '..'), ('T...', 'T', '...')]
actual:   1 [('Erm....', 'Erm', '....'), ('T..', 'T', '..'), ('T...', 'T', '...')]