7

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

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

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

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

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

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

4

3 回答 3

4

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

我不知道 fgrep 的 .NET 实现。如果你找到一个,我当然有兴趣听听。

您可能会追踪 fgrep 源代码(谷歌,有很多来源)并查看它是如何实现的。

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

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

于 2013-08-07T20:27:46.973 回答
3

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

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

这种技术不支持任何使语言非常规的“正则表达式”功能,例如反向引用

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


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

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

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

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

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

于 2013-08-07T19:56:55.243 回答
0

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

该实施可在此处获得:https ://bitbucket.org/tjnieminen/regexkeytrie

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

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

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

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

于 2017-06-14T22:30:49.663 回答