我有一个正则表达式的容器。我想分析它们以确定是否可以生成匹配多于1个的字符串。在编写我自己的正则表达式引擎时,考虑到这个用例,是否有一种简单的方法可以用C ++或Python来解决这个问题?
答案 0 :(得分:32)
没有简单的方法。
只要你的正则表达式只使用标准功能(Perl允许你在匹配中嵌入任意代码,我认为),你可以从每一个产生一个nondeterministic finite-state automaton (NFA),它紧凑地编码RE匹配的所有字符串。
给定任何一对NFA,它们的交集是否为空是可判定的。如果交集不为空,则某些字符串匹配该对中的两个RE(反之)。
标准的可判定性证明是首先将它们确定为DFA,然后构造一个新的DFA,其状态是两个DFA状态的对,并且其最终状态正是其中两个状态的状态。对是他们最初的DFA中的最终对。或者,如果您已经展示了如何计算NFA的补充,那么您可以(DeMorgan的法律风格)通过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的开始状态对开始,例如(A,X)
- 这是交叉点NFA的开始状态。每次第一次访问状态时,按照上述规则为离开这两个状态的每对弧生成一个弧,然后访问这些弧到达的所有(新)状态。您将存储扩展状态弧的事实(例如在哈希表中)并最终探索从一开始就可以到达的所有状态。
如果允许epsilon过渡(不输出字母),那很好:
如果第一个NFA中有A ->epsilon B
,那么对于您到达的每个州(A,Y)
,请为第二个位置的NFA中的epsilons添加弧(A,Y) ->epsilon (B,Y)
。
在将正则表达式转换为NFA时,Epsilon转换在获取两个NFA的并集方面非常有用(但不是必需的);无论何时进行交替regexp1|regexp2|regexp3
,你都可以使用一个NFA,它的起始状态有一个epsilon过渡到每个代表交替中的正则表达式的NFA。
确定NFA的空虚很容易:如果你从开始状态进行深度优先搜索时达到了最终状态,那么它就不是空的。
此NFA交叉点类似于有限状态换能器组成(换能器是输出符号对的NFA,它们成对连接以匹配输入和输出字符串,或将给定输入转换为输出)。
答案 1 :(得分:2)
This regex inverter(使用pyparsing编写)使用re语法的有限子集(例如,不允许*或+) - 您可以将两个re反转为两个集合,然后查找集合交集。 / p>
答案 2 :(得分:-1)
理论上,你描述的问题是不可能的。
实际上,如果您有可管理数量的使用有限子集或正则表达式语法的正则表达式,和/或可用于匹配正则表达式容器的有限字符串选择,您可能能够解决它。
假设您没有尝试解决抽象的一般情况,您可以采取一些措施来解决实际应用问题。也许如果您提供了regexp的代表性示例,并描述了您要匹配的字符串,则可以创建启发式来解决问题。