【问题标题】:How to avoid catastrophic backtracking with this specific pattern如何避免使用这种特定模式的灾难性回溯
【发布时间】:2020-11-17 10:32:00
【问题描述】:

我的前任编写的这个大 reg 模式遇到了灾难性的回溯问题。 我们基本上只是想从职位描述中过滤掉薪水。

这是模式:

(?:(?:(?:\bEURO|\bEuro|\beuro|\bEUR|\bEur|\beur|€)[[:punct:]]?\W*(?:(?<!\d)(?:(\d{1,3}(?:[,.\'\s])\d{3})|([12]?\d{3,5}))(?!\d)(?:[,.](?:\d\d|-|--))?))|(?:(?:(?<!\d)(?:(\d{1,3}(?:[,.\'\s])\d{3})|([12]?\d{3,5}))(?!\d)(?:[,.](?:\d\d|-|--))?)(?:\W*(?:\bEURO|\bEuro|\beuro|\bEUR|\bEur|\beur|€))))|(?:(?:(?:gehalt|lohn|entgelt|netto|brutto|überzahlung|kollektivvertrag|wage\W|salary)\w*\W+(?:[a-zA-Z[:punct:]]+\W+){0,6}(?:(?<!\d)(?:(\d{1,3}(?:[,.\'\s])\d{3})|([12]?\d{3,5}))(?!\d)(?:[,.](?:\d\d|-|--))?))|(?:(?:(?<!\d)(?:(\d{1,3}(?:[,.\'\s])\d{3})|([12]?\d{3,5}))(?!\d)(?:[,.](?:\d\d|-|--))?)(?:\w*\W+){0,6}\w*(?:gehalt|lohn|entgelt|netto|brutto|überzahlung|kollektivvertrag|wage\W|salary)))

它基本上完成了它打算做的事情,但是在我的 Java 应用程序中,它会导致一些 Job 字符串冻结。那一定不能发生。

我几乎可以肯定,这可以做得更容易,而且要好一百倍,我只是没有时间学习高级的正则表达式。也许一些专业人士对如何防止灾难性的回溯问题有一个快速的、第一眼的想法并帮助我。

它应该能识别出类似的模式

Eur 40.000

<some random text>gehalt: 2000-3000

(两个数值,因此我可以进一步处理以确定最小值/最大值)

2000 - 50.000 euro

等等

例子:

输入:

We offer a salary of 40.000 - 50.000.

匹配:

40.000 , 50.000

输入:

date: 12.10.2020 - We offer eur 3000 - 20.000.

匹配:

3000 , 20.000

输入:

Hello world ! Today is: 12.10.2020 - We offer a Gehalt of eur3000-20.000

匹配:

3000 , 20.000

问题出现在疯狂的字符串中,例如 Crazy String that unfortunately can occur during a web crawl

【问题讨论】:

  • 新造币“cacktracking”的积分,但我觉得不得不将其更改为常规的既定术语。
  • 很难猜测像(?&lt;!\d)(?:(\d{1,3}(?:[,.\'\s])\d{3})|([12]?\d{3,5}))(?!\d)(?:[,.](?:\d\d|-|--))? 这样的重复片段真正应该捕捉到什么。可以猜到你希望正则表达式的意思,但如果你能把它拼出来就更好了。
  • 感谢您的示例。不过,您的第一个示例似乎实际上并不匹配。见regex101.com/r/ZflJFQ/1
  • 确实没有,但目前这是次要问题。我主要担心程序不会因为灾难性的回溯而冻结。我将发布一个示例字符串:regex101.com/r/BGXYdP/1 这样的字符串永远不会发生......但不幸的是它们可能会发生......
  • 我没有看到很多回溯的机会,尽管例如(?:\bEURO|\bEuro|\beuro|\bEUR|\bEur|\beur|€)[[:punct:]]?\W*(?:(?&lt;!\d) 非常多余。 [[:punct:]]\W 重叠,因此如果匹配在消耗标点符号时失败,则正则表达式引擎必须尝试将每个标点符号匹配为 \W,这会引入一些但肯定不会 - 孤立地 - 灾难性的回溯。 (?:\b(?:EURO?|[Ee]uro?)|€)\W* 更简洁地匹配相同的字符串,无需回溯((?&lt;!\d) 是多余的,因为无论如何匹配都不能是数字)。

标签: regex backtracking


【解决方案1】:

[:punct:] 表达式匹配标点符号,\W 也可以匹配,因此\W+(?:[a-zA-Z[:punct:]]+\W+){0,6} 必须替换为\W+(?:[a-zA-Z]+\W+){0,6}(?:\w*\W+){0,6} 也容易发生灾难性的回溯,因为 \w* 匹配 0 个或多个单词字符,因此该模式属于 (A+)+ 类型。 替换为\w*(?:\W+\w+){0,6}\W+

--|- 必须缩小为--?,以尽可能少地使用具有相同首字符的替代项。

大量冗余的非捕获组,删除了一些冗余的lookbehinds。

其余的变化只是增强

使用

(?:(?:\bEURO?|€)\W*(\d{1,3}[,.\'\s]\d{3}|[12]?\d{3,5})(?!\d)(?:[,.](?:\d\d|--?))?|(?<!\d)(\d{1,3}[,.\'\s]\d{3}|[12]?\d{3,5})(?!\d)(?:[,.](?:\d\d|--?))?\W*(?:\bEURO?|€))|(?:(?:gehalt|lohn|entgelt|netto|brutto|überzahlung|kollektivvertrag|wage\b|salary)\w*\W+(?:[a-zA-Z]+\W+){0,6}(\d{1,3}[,.\'\s]\d{3}|[12]?\d{3,5})(?!\d)(?:[,.](?:\d\d|--?))?|(?<!\d)(\d{1,3}[,.\'\s]\d{3}|[12]?\d{3,5})(?!\d)(?:[,.](?:\d\d|--?))?\w*(?:\W+\w+){0,6}\W+(?:gehalt|lohn|entgelt|netto|brutto|überzahlung|kollektivvertrag|wage\b|salary))

proof

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多