Products
GG网络技术分享 2025-03-18 16:14 2
正则表达式想要匹配数字和中文数字,但是不能同时匹配这两个
举个例子: 想匹配1001弄和一零零一弄,这两个能识别出来,但是不想识别1零零1弄这种,有什么好的方式吗?
我写的是 String regexp = "[\\d | 〇一二三四五六七八九十零壹贰叁肆伍陆柒扒玖拾百千万]+号";
这种会匹配到混合的中文和数字,除了分开匹配,有没有好的解决方法?
'一零零一弄'.match(/[\\d]+弄|[〇一二三四五六七八九十零壹贰叁肆伍陆柒扒玖拾百千万]+弄/)
可能有人会不屑于回答这样的问题,就算回答,也是一句让人看不懂的话,比如:
因为正则表达式的代码是type-2的,你用只有type-3能力的正则表达式去匹配一个type-2的正则表达式代码,必然会匹配出很多不是正则表达式的东西,无法精确匹配。
稍微翻译一下的话,这位大牛的意思是这样:
正则表达式只能匹配正则文法(假设你在问的是“正统”的正则表达式,而不是perl中那样被扩展过的版本),而正则表达式本身是上下文无关文法,所以没法用正则表达式来匹配正则表达式。
(关于type-2, type-3的意思,可以见这里:
乔姆斯基谱系)
更具体一点说,因为正则表达式中有匹配的括号,所以正则表达式不属于正则文法。因为正则文法是没法匹配配对的括号的。
说到这里,这个问题应该可以算回答得比较完整了。或许读者你也知道这个答案,但是不知道你是否思考过:为什么正则表达式不能匹配配对括号?如果你不知道这个问题的答案,那就接着往下看吧。
我假设你知道正则表达式都可以转化成对应的NFA(如果你不知道,以下是我现搜出来的一些链接:
非确定有限状态自动机的构建(一)——NFA的定义和实现,
编译原理,
基于ε-NFA的正则表达式引擎),所以这个问题就相当于为什么NFA不能匹配括号。
NFA,非确定性有限状态自动机,有一个重要的特性就是有限。NFA只有有限个状态,而对于每一个输入字符,NFA都必然会发生一次状态转移。根据鸽笼原理,我们很容易知道,对于一个特定NFA,我们总能找到一个足够长的字符串,让这个NFA在接受这个串之前经过某一个状态两次。
假设我们的输入串是w,我们可以把它分成三块xyz。当NFA处理完了x这部分之后,到达了状态S,而且当处理完了xy这部分之后,我们又回到了状态S。一个NFA对输入的所有“记忆”都包含在其状态之中。既然处于S状态下的NFA能接受字符串y然后回到S,那么它应该还能再接受一个y并且再回到S。也就是说,这个NFA不仅会接受字符串xyz,还应该会接受xz,xyyz,xyyyz,xyyyyz,\\ldots
同样的,根据鸽笼原理,我们知道xy部分的长度不会超过这个NFA内部状态数+1。
知道了这些,我们就能进入最后的步骤了。假设现在你找到了一个能匹配配对括号的NFA,设这个NFA共有p个状态,我们任取一个匹配的括号串:
((\\ldots((()))\\ldots))
满足其中左括号的个数大于p+1。显然你的NFA必须接受这个输入串。但是根据我们之前得到的结论,我们应该能把这个输入串分成xy=((((((\\ldots(,z=((\\ldots))\\ldots)),保证这个NFA也接受xz,xyyz,xyyyz,xyyyyz,\\ldots而这些串显然不是匹配的括号串。于是得出矛盾,证明这样的NFA不存在。
P.S.
这个证明中用到的这个技巧叫做
Pumping lemma,因为过程中不断重复y的过程被叫做“pumping”。而它在中文中被叫做“泵引理”,这个翻译显然不是很好,但我也想不出什么更好的建议。也许应该学日文翻译一样,叫它“反复引理”?
Demand feedback