其他教程

其他教程

Products

当前位置:首页 > 其他教程 >

Drools 正则表达式匹配不准

GG网络技术分享 2025-03-18 16:14 0


问题描述:

Drools matches 匹配标准java正则表达式不准确

Drools matches 匹配正则表达式未命中,但是java的正则表达式会命中,请问知道是什么原因吗?

Drools 版本号:6.3.0.Final

正则表达式语法:

(?:(?:万)[\\s\\S]{0,12}(?:六))

匹配的文本内容:因为他总共还有好几万没有处理他这一期六千块钱的话

采用java语言正则表达式可以匹配命中:

System.out.println(Pattern.compile(\"(?:(?:万)[\\\\s\\\\S]{0,12}(?:六))\").matcher(\"因为他总共还有好几万没有处理他这一期六千块钱的话\").find());

使用drools标准语法匹配未命中
this[\"text\"] matches \"(?:万[\\\\s\\\\S]{0,12}(?:六))\"
drools正则表达式感觉是默认加上了正则表达式 ^ 开头,如果文本内容改成上面正则表达式 “万” 开头就可以命中,请问这是一个bug吗?还是我的使用有问题呢?
如何才能让Drools正则表达式和java的正则表达式一样命中上面的语法呢?

网友观点:

你显示的加上^或者*号用来区分是否必须从头开始匹配

是否存在能匹配一个字符串中所有正则表达式的正则表达式?为什么?

可能有人会不屑于回答这样的问题,就算回答,也是一句让人看不懂的话,比如:

因为正则表达式的代码是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