【问题标题】:Is there a data structure that provides lookup by pattern (regex)?是否有提供按模式(正则表达式)查找的数据结构?
【发布时间】:2013-08-07 19:51:11
【问题描述】:

我已经多次遇到这种情况:某些文本可能会匹配多种模式,而您想根据它是哪种模式来做一些特定的事情。

在过去,我总是只使用正则表达式列表并迭代直到找到匹配项。

我想知道是否有更有效的数据结构。例如,如果我使用 C#,则使用带有正则表达式键的字典。

我意识到,如果模式都是前缀或后缀,那么像 Trie 这样的东西是有意义的。不过,我不清楚这是否适用于一般情况。

在我看来,关键冲突可能存在一些歧义;例如,如果某些文本匹配多个模式,应该返回什么? (我认为在这种情况下,也许一个非确定性的结果是可以的;但只要记录了行为,我就可以接受。)

无论如何,这样的数据结构是否存在,无论是在 .NET 中还是在其他地方?

【问题讨论】:

  • 可能不是您要查找的内容,但如果使用 c# 列表,请使用 FirstOrDefault(x=>doesItMatch(x))。仍然必须遍历每一个,但它会在第一个有效的时候停止。
  • 我不确定以比 O(n) 更好的方式做到这一点的可能性有多大。
  • @SLaks:我也不确定!但我之前曾被聪明的数据结构所震撼。
  • 如果正则表达式可以通过它们的逻辑排他性(例如,任何不匹配模式 A 的东西不可能匹配模式 B)组织成一个逻辑树,那么你可以减少比较。树最简单的数据结构是链表。

标签: c# .net regex data-structures


【解决方案1】:

fgrep 工具正是您所说的:将文本与多个正则表达式匹配。我的理解是原始版本使用了与Aho-Corasick string matching algorithm 非常相似的东西来一次搜索多个正则表达式。基本上,它创建了一个 DFA 并运行它。

我不知道 fgrep 的 .NET 实现。如果你找到了,我当然会很感兴趣。

您可以追踪 fgrep 源代码(Google 可以找到它,有很多来源)并查看它是如何实现的。

或者,您可以让您的程序外壳输出到 fgrep。或者也许创建一个 C++ DLL,它有一个 fgrep 入口点,您可以从 C# 程序中调用该入口点。

如果您的多个模式是常量字符串(即不是正则表达式),那么您可能会对我的C# implementation of the Aho-Corasick algorithm 感兴趣。

【讨论】:

    【解决方案2】:

    让我们假设这些正则表达式是真正的正则表达式。然后每个都可以转换为Nondeterministic Finite Automaton,可以将converted转换为Deterministic Finite Automaton,可以在输入长度的O(n)时间内进行评估。

    但它并没有解决同时匹配多个正则表达式的问题。我们可以通过创建一个如下所示的单个正则表达式来做到这一点:(regexp1|regexp2|...),并将 that 转换为单个 NFA/DFA。在自动机的分支中添加一些工具,以跟踪哪个特定的正则表达式生成了与输入匹配的路径,并且您已经有了匹配器,输入字符串的长度仍然为 O(n)。

    此技术不支持任何使语言不规则的“正则表达式”功能,such as backreferences

    另一个缺点是生成的 DFA 可能很大。也可以直接评估 NFA,这可能会更慢但具有更好的记忆行为。


    实际上,用代码表达这个想法也很容易,不用担心自动机的东西。只需使用匹配组:

    combined_regexp = (regexp1)|(regexp2)|...
    

    在评估时,只需查看哪个组与输入匹配。

    请记住,大多数正则表达式实现/库在某些极端情况下的行为非常糟糕,在这种情况下,它们可能需要指数级的时间来编译或匹配正则表达式。我不确定在实践中有多少问题。 Google 的 RE2 库是专门为不具有这种病态行为而设计的,但可能还有其他的。

    另一个问题可能是,除非您的正则表达式实现专门宣传 O(n) 行为,否则它可能只是依次尝试每个替代方案。在那种情况下,这种方法不会给您带来任何好处。

    【讨论】:

    • 我自己也在考虑这些方面的事情(构建一个大的“主”正则表达式)。你知道任何现有的实现吗?我猜没有,因为你没有提到任何。
    • 不,我不知道。我所知道的唯一真正构建内存自动机的正则表达式实现是谷歌的RE2
    【解决方案3】:

    几年前,我实现了一个基于确定性有限状态机的正则表达式搜索引擎,这与 Thomas 在他的回答中所描述的非常相似。它将正则表达式键和值的列表编译为单个有限状态自动机,该自动机在其终止状态中引用已定义类型的对象(例如,RegexTrie 在终止状态时引用字符串)。

    这里提供了实现:https://bitbucket.org/tjnieminen/regexkeytrie

    标准搜索是通过维护通过机器的路径列表来执行的。对于搜索文本的每个字符,每个活动路径都会前进,并且每当达到终端状态时都会记录匹配项。为源文本的每个字符添加一个从机器根开始的新路径(这允许子字符串匹配),并且从路径列表中删除停止在非终端状态的路径。

    通常最好为任何任务自定义处理。例如,如果使用引擎进行正则表达式替换,最好在遍历期间生成编辑后的文本(如转换器),而不是使用返回的匹配项执行查找和替换操作。

    我已经针对普通的 .Net 正则表达式对实现进行了基准测试,它似乎在应该的情况下做得更好,即结合大量正则表达式和要搜索的长文本。

    该实现尚未经过彻底测试,因此可能存在一些错误,并且使用复杂的正则表达式可能很容易耗尽内存(或者它们可能需要很长时间才能编译)。但由于目前没有类似的可用产品,对于寻找它提供的性能特征的人来说,它可能是一个有用的起点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-29
      • 2016-03-01
      • 1970-01-01
      • 2016-06-26
      • 2015-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多