更新:此问题已由开发人员在提交 be893e9中解决
如果遇到相同的问题,请更新regex
模块。
您需要版本2017.04.23
或更高版本。
正如这个答案所指出的,我需要这个正则表达式:
(?i)b((w{1,3})(-|.{2,10})[t ]?)+(2w{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 ]?)+(2w{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
似乎这个错误与回溯有关。当捕获组重复时,会发生此问题,并且捕获组匹配,但组之后的模式不匹配。
举个例子:
>>> 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 ]?)+(2w{2,})"
>>> regex.sub(pattern, substitute, content)
'"Erm....yes. T-Thank you for that."'
编辑:该错误现已在正则表达式 2017.04.23 中解决
刚刚在 Python 3.6.1 中进行了测试,原始模式在re
和regex
中的工作方式相同
原始解决方法 - 您可以使用惰性运算符+?
(即在T...Tha....Thank
等边缘情况下行为与原始模式不同的不同正则表达式):
pattern = r"(?i)b((w{1,3})(-|.{2,10})[t ]?)+?(2w{2,})"
2017.04.05 中的错误是由于回溯造成的,如下所示:
不成功的较长匹配会创建空的2
组,从概念上讲,它应该触发回溯到较短的匹配,其中嵌套组不会为空,但regex
似乎"优化"并且不会从头开始计算较短的匹配,而是使用一些缓存的值,忘记撤消嵌套匹配组的更新。
贪婪匹配示例((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', '...')]