【问题标题】:Why is this regex experiencing catastrophic backtracking?为什么这个正则表达式会经历灾难性的回溯?
【发布时间】:2015-06-19 19:42:49
【问题描述】:

我已经阅读了 the articlesother questions 关于正则表达式中的灾难性回溯以及嵌套 +* 量词如何导致它的内容。然而,我的正则表达式仍然遇到灾难性的回溯没有嵌套量词。有人可以帮我理解为什么吗?

我编写这些正则表达式是为了在威尔士诗歌中搜索a specific type of rhyme。押韵由行首的所有辅音在结尾重复组成,并且开头和结尾的辅音之间必须有一个空格。我已经删除了所有的元音,但是有两个例外使这些正则表达式变得丑陋。首先,允许中间有不重复的辅音,如果有,那就是另一种押韵。其次,字母 m、n、r、h 和 v 可以打断押韵(出现在开头而不出现在结尾,反之亦然),但它们不能被忽略,因为有时押韵只包含那些字母。

我的脚本会自动为每一行构建一个正则表达式并对其进行测试。它在其余时间都有效,但是这一行正在造成灾难性的回溯。该行没有元音的文本是:

nn  Frvvn  Frv v

正则表达式自动发现nn FrvvnFrv v 押韵,因此它会使用后面所需的最后一个字母(Frvvn 中的n)再次尝试。如果不需要,则可以缩短押韵。这是正则表达式:

^(?P<s_letters>         # starting letters
[mnrhv]*?\s*n{0,2}      # any number of optional letters or any number
                        # of spaces can come between rhyming letters
[mnrhv]*?\s*n{0,2}
[mnrhv]*?\s*F{1,2}
[mnrhv]*?\s*[rR]?(?:\s*[rR])? # r can also rhyme with R, but that's
                              # not relevant here (I think)
[mnrhv]*?\s*v{0,2}
[mnrhv]*?\s*v{0,2}
[mnrhv]*?\s*n{1,2}
[mnrhv\s]*?)
(?P<m_letters>          # middle letters
[^\s]*?(?P<caesura>\s)  # the caesura (end of the rhyme) is the
                        # first space after the rhyme     
.*)                     # End letters come as late as possible
(?P<e_letters>          # End group
[mnrhv]*?\s*n{0,2}
[mnrhv]*?\s*n{0,2}
[mnrhv]*?\s*F{1,2}
[mnrhv]*?\s*[rR]?(?:\s*[rR])?
[mnrhv]*?\s*v{0,2}
[mnrhv]*?\s*v{0,2}
[mnrhv]*?\s*n{1,2}
[mnrhv\s]*?)$

即使它没有任何嵌套的量词,它仍然需要很长时间才能运行。以相同方式生成的其他行的正则表达式运行得很快。这是为什么呢?

【问题讨论】:

  • Wayyy 对许多重叠的可选内容。您应该能够将其减少到几行。
  • 有一个使用正则表达式的一般规则。任何可选的复合子表达式都应该包含非可选的内容。这有助于引擎对齐这些常数。它们被称为伪锚。最好的方法是将选项包含在一个集群组中,并通过交替将它们彼此分开。尝试隔离不同的表格并按照每个表格进行匹配。避免将可选的简单构造相互串联。

标签: python regex performance


【解决方案1】:

我没有看到任何嵌套的量词,但我看到了很多会导致高指数多项式运行时的歧义。例如,考虑这部分正则表达式:

[mnrhv]*?\s*[rR]?(?:\s*[rR])? # r can also rhyme with R, but that's
                              # not relevant here (I think)
[mnrhv]*?\s*v{0,2}
[mnrhv]*?\s*v{0,2}
[mnrhv]*?\s*n{1,2}
[mnrhv\s]*?)
(?P<m_letters>          # middle letters
[^\s]*?(?P<caesura>\s)  # the caesura (end of the rhyme) is the

假设正则表达式引擎在这一点上,它看到的文本只是ns 的一大块。那些ns 可以分为正则表达式的以下部分:

[mnrhv]*?\s*[rR]?(?:\s*[rR])?
^^^^^^^^^

[mnrhv]*?\s*v{0,2}
^^^^^^^^^

[mnrhv]*?\s*v{0,2}
^^^^^^^^^
[mnrhv]*?\s*n{1,2}
^^^^^^^^^   ^^^^^^
[mnrhv\s]*?)
^^^^^^^^^^^
(?P<m_letters>
[^\s]*?(?P<caesura>\s)
^^^^^^^

如果ns 的数量是N,那么有O(N**6) 方法来划分ns,因为这里有6 个*? 块匹配n,以及介于两者之间的所有内容是可选的或者也匹配n

那些\s 部分是强制性的吗?如果是这样,您可以通过在它们上放置 + 而不是 * 来改进运行时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-05
    • 1970-01-01
    • 2016-03-24
    • 1970-01-01
    • 2017-09-24
    相关资源
    最近更新 更多