2017-04-22 57 views
11

更新:此问题是由开发商在commit be893e9Python的正则表达式模块VS重新模块 - 模式不匹配

如果你遇到了同样的问题得到解决,更新您的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

澄清:你需要这个表达式与__both__'re'和'工作正则表达式“,还是只用'正则表达式”? –

+0

另外,从字面上看,'regex'的pypi页面上的第一句话是*“这个正则表达式的实现是向后兼容标准的're'模块”*,所以有人已经报告过这个错误? –

+2

为什么使用'(?<= \ b)'而不是'\ b'这是一个零长度的断言。 –

回答

4

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

一个例子:

>>> 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."' 
+0

嗯,但预计是'“呃....是的.-谢谢你。”# – Aprillion

+0

@Aprillion好的赶上,谢谢。不知道为什么会这样。 –

+0

@Rawing你仍然有一个错字,你产生的输出是''“呃....是的.-谢谢你。”' ' –

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年4月5日的错误是由于回溯,是这样的:

不成功的再匹配创建空\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', '...')] 
+0

这不是真的是一个解决方法,这是对模式的修改。如果你用'a-abc-abcxy'这样的输入来尝试它,它将产生与原始模式不同的输出。 –

+0

@Rawing谢谢你指出。我做了一些有关这种模式的原始用例的测试(请参阅此处https://stackoverflow.com/questions/43560759/python-regex-replacing-string-that-should-not-match),并且确实有可能出现不希望的输出将会被制作成例如T ... Tha ....感谢 –

+0

没错,不是一个解决方案,只是当你不需要处理'T ...... Tha .... Thank'和/或如果' T-Tha ......感谢'输出就像'Tha-Thank'一样好(对我来说两者都没有意义,所以我会给我的解决方法一个机会,并询问客户该解决方法对他们来说是否足够好) – Aprillion