如何检测两个正则表达式在它们可以匹配的字符串中是不是重叠?

Posted

技术标签:

【中文标题】如何检测两个正则表达式在它们可以匹配的字符串中是不是重叠?【英文标题】:How can you detect if two regular expressions overlap in the strings they can match?如何检测两个正则表达式在它们可以匹配的字符串中是否重叠? 【发布时间】:2009-12-04 20:26:42 【问题描述】:

我有一个正则表达式容器。我想分析它们以确定是否有可能生成一个与其中超过 1 个匹配的字符串。没有考虑到这个用例编写我自己的正则表达式引擎,C++ 或 Python 中有没有简单的方法来解决这个问题?

【问题讨论】:

为了解决这个问题,了解正则表达式的确切含义可能很重要。您是在谈论特定编程语言使用的正则表达式语法吗?或者更确切地说,“真正的”正则表达式仅使用连接、交替和 Kleene 星号? 我很想知道最强大的正则表达式类型。 嗨约瑟夫,我实际上正在研究与这个问题相关的博士论文。我想知道您是否可以详细说明您正在追求的应用程序。如果需要,我们可以通过电子邮件进一步讨论:mwehar at buffalo dot edu。 【参考方案1】:

没有简单的方法。

只要您的正则表达式仅使用标准功能(我认为 Perl 允许您在匹配中嵌入任意代码),您就可以从每个表达式生成一个 nondeterministic finite-state automaton (NFA),它紧凑地编码 RE 匹配的所有字符串。

给定任意一对 NFA,可以判断它们的交集是否为空。如果交集不为空,则某些字符串匹配对中的两个 RE(反之亦然)。

标准的可判定性证明是先将它们确定为DFAs,然后构造一个新的 DFA,其状态是两个 DFA 的状态对,其最终状态恰好是这对中的两个状态最终在他们原来的 DFA 中。或者,如果您已经展示了如何计算 NFA 的补码,那么您可以(德摩根定律风格)通过 complement(union(complement(A),complement(B))) 获得交集。

不幸的是,NFA->DFA 涉及潜在的指数级爆炸(因为 DFA 中的状态是 NFA 中状态的子集)。来自Wikipedia:

一些常规语言类可以 只能用确定性来描述 大小增长的有限自动机 的大小呈指数级增长 最短等效正则 表达式。标准示例是 这里的语言 L_k 由 字母表 a,b 上的所有字符串 其倒数第 k 个字母等于 a。

顺便说一句,你绝对应该使用OpenFST。您可以将自动机创建为文本文件并尝试最小化、交叉等操作,以了解它们对您的问题的效率。已经存在开源 regexp->nfa->dfa 编译器(我记得一个 Perl 模块);修改一个以输出 OpenFST 自动机文件并播放。

幸运的是,可以避免状态子集爆炸,并使用与 DFA 相同的构造直接与两个 NFA 相交:

如果A ->a B(在一个NFA中,你可以从状态A到B输出字母'a')

X ->a Y(在另一个 NFA 中)

然后在路口(A,X) ->a (B,Y)

(C,Z) 是最终的,如果 C 在一个 NFA 中是最终的并且 Z 在另一个 NFA 中是最终的。

要启动该过程,您需要从两个 NFA 的启动状态对开始,例如(A,X) - 这是交叉口 NFA 的开始状态。每次您第一次访问一个状态时,根据上述规则为离开这两个状态的每对弧生成一个弧,然后访问这些弧到达的所有(新)状态。您将存储扩展状态弧的事实(例如,在哈希表中)并最终探索从一开始就可以到达的所有状态。

如果您允许 epsilon 转换(不输出字母),那很好:

如果A ->epsilon B 在第一个 NFA 中,那么对于您到达的每个状态 (A,Y),添加弧 (A,Y) ->epsilon (B,Y) 并且对于第二位置 NFA 中的 epsilons 类似。

在将正则表达式转换为 NFA 时,Epsilon 转换在合并两个 NFA 时很有用(但不是必需的);每当您有交替 regexp1|regexp2|regexp3 时,您就采用联合:一个 NFA,其起始状态具有到每个 NFA 的 epsilon 转换,代表交替中的正则表达式。

判断 NFA 是否为空很容易:如果您从开始状态进行深度优先搜索时达到了最终状态,那么它就不是空的。

这种 NFA 交点类似于有限状态转换器组合(转换器是输出符号对的 NFA,它们成对连接以匹配输入和输出字符串,或将给定的输入转换为输出)。

【讨论】:

很好的答案,虽然这几乎正是我希望避免自己编写代码的原因;) OpenFST 看起来很整洁。【参考方案2】:

This regex inverter(使用 pyparsing 编写)使用有限的 re 语法子集(例如,不允许 * 或 +) - 您可以将两个 re 反转为两个集合,然后寻找一个集合交集。

【讨论】:

链接已断开:/ 谢谢,我试图追查所有这些参考资料,但看起来我错过了一些。答案已被编辑以指向新的存储库。【参考方案3】:

理论上,你描述的问题是不可能的。

在实践中,如果您有可管理数量的正则表达式使用有限的子集或正则表达式语法,和/或可用于匹配正则表达式容器的字符串选择有限,您也许可以解决它。

假设您不尝试解决抽象的一般情况,您可能可以采取一些措施来解决实际应用。也许如果您提供了一个有代表性的正则表达式样本,并描述了您要匹配的字符串,就可以创建一个启发式来解决问题。

【讨论】:

"regexps" 识别上下文无关或更糟糕的语言确实无法证明交集处没有字符串。显然,这些并不是真正的“正则表达式”,而是受它们启发的东西。但是可以使用 NFA 捕获完整的正则表达式语言,以及人们实际使用的大部分内容来自“regexp”。 我同意正则表达式可以分解为 NFA,我只是认为原始发布者可能有更具体的用例,例如像“c[aeiou]t”和“d”这样的正则表达式[aeiou]g",允许的字符串是英文字典。

以上是关于如何检测两个正则表达式在它们可以匹配的字符串中是不是重叠?的主要内容,如果未能解决你的问题,请参考以下文章

使用正则表达式检测词组搭配

正则表达式,不匹配多个长度不等字符串

java正则表达式中是啥意思

用java正则表达式检测字符串中是不是含有某字符

如何检查字符串是不是与 node.js 中的任何正则表达式数组匹配?

需要正则表达式来匹配两个单词,因为它们之间可能有任意数量的空格或其他字符