繁体   English   中英

正则表达式的 100% CPU 使用率取决于输入长度

[英]100% CPU usage with a regexp depending on input length

我试图在 Python 中提出一个正则表达式,它必须匹配任何字符,但要避免三个或更多连续的逗号或分号。 换句话说,最多只能使用两个连续的逗号或分号。

所以这就是我目前拥有的:

^(,|;){,2}([^,;]+(,|;){,2})*$

它似乎按预期工作:

>>> r.match('')
<_sre.SRE_Match object at 0x7f23af8407e8>
>>> r.match('foo,')
<_sre.SRE_Match object at 0x7f23af840750>
>>> r.match('foo, a')
<_sre.SRE_Match object at 0x7f23af8407e8>
>>> r.match('foo, ,')
<_sre.SRE_Match object at 0x7f23af840750>
>>> r.match('foo, ,,a')
<_sre.SRE_Match object at 0x7f23af8407e8>
>>> r.match('foo, ,,,')
>>> r.match('foo, ,,,;')
>>> r.match('foo, ,, ;;')
<_sre.SRE_Match object at 0x7f23af840750>

但是当我开始增加输入文本的长度时,正则表达式似乎需要更多时间来给出响应。

>>> r.match('foo, bar, baz,, foo')
<_sre.SRE_Match object at 0x7f23af8407e8>
>>> r.match('foo, bar, baz,, fooooo, baaaaar')
<_sre.SRE_Match object at 0x7f23af840750>
>>> r.match('foo, bar, baz,, fooooo, baaaaar,')
<_sre.SRE_Match object at 0x7f23af8407e8>
>>> r.match('foo, bar, baz,, fooooo, baaaaar,,')
<_sre.SRE_Match object at 0x7f23af840750>
>>> r.match('foo, bar, baz,, fooooo, baaaaar,,,')
>>> r.match('foo, bar, baz,, fooooo, baaaaar,,,,')
>>> r.match('foo, bar, baz,, fooooo, baaaaar, baaaaaaz,,,,')

最后它在这个阶段完全卡住了,CPU 使用率上升到 100%。

我不确定是否可以优化正则表达式或是否涉及其他内容,感谢任何帮助。

你遇到了灾难性的回溯

这样做的原因是您已将分隔符设为可选,因此您的正则表达式的[^,;]+部分(它本身在重复组中)将在最终不得不承认失败之前尝试( baaaaaaaz )的大量排列当遇到两个以上的逗号时。

RegexBuddy在正则表达式引擎的 1.000.000 步后使用您的最后一个测试字符串中止匹配尝试。 Python 会继续努力。

想象一下字符串baaz,,,

尝试您的正则表达式,正则表达式引擎必须检查所有这些:

  1. baaz,,<failure>
  2. baa + z,,<failure>
  3. ba + az,,<failure>
  4. ba + a + z,,<failure>
  5. b + aaz,,<failure>
  6. b + aa + z,,<failure>
  7. b + a + az,,<failure>
  8. b + a + a + z,,<failure>

在宣布全面失败之前。 看看这如何随着每个额外的字符呈指数增长?

使用所有格量词或原子组可以避免此类行为,遗憾的是 Python 当前的正则表达式引擎不支持这两种行为。 但是您可以轻松地进行反向检查:

if ",,," in mystring or ";;;" in mystring:
    fail()

根本不需要正则表达式。 如果,;,和类似的情况也可能发生并且应该被排除,那么使用 Andrew 的解决方案。

我认为以下应该做你想要的:

^(?!.*[,;]{3})

如果字符串包含三个或更多,这将失败,或者; 连续。 如果您确实希望它匹配一个字符,请添加一个. 在最后。

这利用了负前瞻,如果正则表达式.*[,;]{3}匹配,这将导致整个匹配失败。

试试这个正则表达式:

^([^,;]|,($|[^,]|,[^,])|;($|[^;]|;[^;]))*$

它重复匹配:

  • 一个既不是,也不是的单个字符; , 或者
  • a ,后面没有另一个,或 a ,,后面没有另一个, , 或
  • 一个; 要么没有跟随另一个; 或一个;; 后面没有另一个;

直到到达终点。 它非常有效,因为它会提前失败而无需进行太多回溯。

这个想法如何匹配那些你不想要的模式".+,,,"在 Python 中只保留那些不匹配的。 应该快

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM