更新:开发人员在 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
答案 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中测试过,原始模式在re
和regex
原始解决方法 - 您可以使用惰性运算符+?
(即在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', '...')]