【问题标题】:Searching a Collection of Regexes Efficiently有效地搜索正则表达式集合
【发布时间】:2016-11-17 16:48:11
【问题描述】:

如果我有一些未知数量的正则表达式(零个或更多,希望少于几千个)搜索与给定字符串匹配的正则表达式的有效方法是什么?

我应该使用哪些类型的容器、算法和/或数据结构?如果我想找到唯一匹配的正则表达式与我想找到所有正则表达式匹配,这有什么不同吗?这与只想知道匹配的数量有什么不同吗?

让我换一种说法,假设我有一个用户输入任意字符串,并且我有一些正则表达式容器。我可以以任何我选择的方式设计容器,并以我选择的任何方式进行搜索。如果我想要一个与该集合中的用户输入匹配的所有正则表达式的列表,我应该怎么做?如果我只想知道存在多少匹配项怎么办?如果我只是想确保匹配的唯一性怎么办?

【问题讨论】:

  • 将它们组合成一个表达式,根据需要“捕获”原始表达式。
  • 这些(从数学上讲)是正则表达式,还是根据大多数正则表达式库,它们是随机的图灵完备匹配函数集?它们是完全匹配还是子字符串匹配?
  • @rici PCRE/ECMAScript 正则表达式和完全匹配。但我很好奇所有变体的答案。

标签: regex algorithm language-agnostic containers


【解决方案1】:

如果您可以在尝试匹配字符串之前对正则表达式进行一些预计算,那么您可以将所有正则表达式的并集转换为 DFA,该 DFA 可以同时将字符串与所有正则表达式匹配。

见:https://en.wikipedia.org/wiki/Deterministic_finite_automaton

这种方法经常用于解析器和编译器中的词法分析(标记化)。 DFA 的好处是,无论您在其中放入多少正则表达式或它们有多复杂,它的速度都是相同的(快)。

这不是那么容易,但周围有工具。如果您正在使用 Java,那么我有一个您可以使用的开源项目:http://mtimmerm.github.io/dfalex/。要回答您的其他问题,您可以根据需要从中获取所有匹配的正则表达式的集合。

如果您对如何自己进行操作感兴趣,该过程通常包括使用 Thompson 构造 (https://en.wikipedia.org/wiki/Thompson%27s_construction) 将正则表达式转换为 NFA (https://en.wikipedia.org/wiki/Nondeterministic_finite_automaton),然后使用以下方法将 NFA 转换为 DFA子集构造(https://en.wikipedia.org/wiki/Powerset_construction),然后通常使用 Hopcroft 算法最小化 DFA(https://en.wikipedia.org/wiki/DFA_minimization

还有很多优化和技巧的空间。

祝你好运!

附:我应该注意几件事:1)通常不能用具有反向引用的正则表达式制作 DFA。 2)理论上,DFA 可能呈指数级增长。这几乎不会偶然发生,但如果您的正则表达式是由潜在的恶意人员输入的,那么您将不得不对这种可能性采取一些措施。

【讨论】:

  • 这很好,我不知道 DFA 最小化是一回事。至于我的潜在恶意用户,他们大多是在伤害自己。
【解决方案2】:

除非几天之内没有人击败它,否则我不会将自己的答案标记为答案。


到目前为止,我唯一有价值的想法是在添加时将正则表达式放入容器内的两堆之一中。

每个正则表达式都带有一些通配符、字符类或其他任何使其偏离传统字符串的东西,都被归为一堆。我将其称为 RegexPile

在另一堆中,所有的正则表达式都是字符串或可以简单地转换为字符串。因为字符串很容易匹配并且算法很好理解,所以我可以说这个堆将是有序的容器,并且会被排序,并且在其中查找字符串对于二进制搜索来说是微不足道的。我将其称为 SortedStringArray

天真地,我可以线性搜索 RegexPile 并在 SortedStringArray 上进行二分搜索。这至少允许我跳过一些比较并且在时间或空间方面花费很少,但也没有太多真正的优化。

它在计算上是相似的,但如果我做这样的事情,我想我会为 RegexPile 中的每个正则表达式(或每个正则表达式小组)启动一个线程。我的想法是,任何给定的正则表达式都可以采用无限量,因为正则表达式可以做到这一点。然后,如果任何线程花费的时间太长,我可能会因超时而失败并过早终止所有线程。我还认为大多数人会在第一个字符上失败,这意味着一旦检查了第一个字符,大多数线程就会消失。使用当今大多数系统提供的廉价写时复制线程,该线程生成应该足够便宜,以至于许多线程将在我完成所有生成之前关闭,并且只有相当相似的线程才会在任何时候逗留。然后我在另一个线程中为 SortedStringArray 执行二进制。

【讨论】:

    【解决方案3】:

    一个 PHP 示例:

    <?php
    $regex_array = array(
      "/regex_1/" => 0,
      "/regex_2/" => 0,
      "/regex_3/" => 0    // and so on and so forth
    );
    
    $strings_array = array(
      "input_string_1",
      "input_string_2",
      "input_string_3"    // and so on and so forth
    );
    
    foreach ($regex_array as $key => $value)
      foreach ($strings_array as $current_string)
        if (preg_match($key, $current_string))
          $regex_array[$key]++;
    ?>
    

    Here 是运行代码。

    【讨论】:

    • 几年没接触PHP了,这不就是线性搜索吗?
    猜你喜欢
    • 1970-01-01
    • 2016-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-02
    • 2023-04-10
    相关资源
    最近更新 更多