正则表达式模式匹配

发表于:2007-05-26来源:作者:点击数: 标签:
关于正则表达式的一个问题 首先明确一下什么是匹配 匹配可以从两个方面来理解 1 一个正则表达式和一个字符串是否匹配 在perl中 =~ 代表捆绑 也就是让正则表达式去匹配指定的字符串 如果如果用匹配的符号形式来表现 对于 /regular/ =~ string 如果返回值为真
关于正则表达式的一个问题

首先明确一下什么是匹配
匹配可以从两个方面来理解
1 一个正则表达式和一个字符串是否匹配
   在perl中 =~ 代表捆绑 也就是让正则表达式去匹配指定的字符串
   如果如果用匹配的符号形式来表现 对于 /regular/ =~ "string"  如果返回值为真 则说/regular/ 匹配了"string"
2 一个正则表达式不只可以匹配一个字符串
   例如  /a*b/   不仅可以匹配aaaab 还可以匹配aaaaaab     b 等
   这里所谓的“匹配”也是指  /a*b/ =~ aaaab   /a*b/ =~ b  返回值都是真
下面说说依次匹配
首先的一个问题是一个正则表达式的工作过程
举个例子

代码:
$data is 192.168.0.1
$data =~ /([0-9]*\.)([0-9]*.)([0-9]*\.)([0-9])/;
is 192.
is 168.
is 0.
is 1
这里
/([0-9]*\.)([0-9]*\.)([0-9]*\.)([0-9])/  匹配了192.168.0.1
同时在这个较长的正则表达式子是由几个短的正则表达式组成的,这些短的在()中的正则表达式称为模式,大家对,,,的值的输出都非常理解

下面再举一个例子
代码:
$data =~ /([0-9]*\.)([0-9]*\.)*([0-9])/;
is 192.
is 0.
is 1

这里有观点解释到这里([0-9]*\.) ([0-9]*\.) *([0-9])  红色部分"依次匹配" 了168.    0.       所以是0.  
这并不是很好理解
有证:
3x3eyes 写到:
....匹配.168都还好理解所谓的依次匹配是什么意思,请各位帮忙

问题的关键是为什么这里的红色部分会去依次匹配168.     0.呢?regular 有这种依次匹配的机制吗 它在什么情况下会这样做 什么情况下不会这么做呢
下面我们通过一个例子来验证regular 会不会有这种依次匹配的机制
代码:
$data =~ /([0-9]*\.)([0-9]*\.)([0-9])/;
is 192.
is 168.
is 0

这里红色部分并没有去依次匹配168.  0. 而是直接匹配了168.  
为什么呢 看来 问题出现在* 上面
也就是当正则表达式是 $data =~ /([0-9]*\.)([0-9]*\.)*([0-9])/;   的时候  红色部分依次匹配了168.   0.   并最终把最后匹配的0.存到了分组2中
     当正则表达式是 $data =~ /([0-9]*\.)([0-9]*\.)([0-9])/;    的时候  红色部分就不再依次匹配了??  它就匹配168.  并把168存在分组2中 这是什么道理?  
正则表达式有一个引擎 引擎的工作原理细节没有找到相应文档 但是对于这种情况 可以试着解释为
代码:
对于 $data =~ /([0-9]*\.)([0-9]*\.)([0-9])/;
这时候对于模式1 模式2 模式3 他们没有机会选择  分别将各自匹配的 192.   168.   0  存在正则表达式的记忆体 中
而对于 $data =~ /([0-9]*\.)([0-9]*\.)*([0-9])/;
对于模式1  从左到右搜索$data 匹配了192.    把它存在了记忆体  中
对于模式2  从左到右搜索剩下的$data 部分  匹配了 168.  并把它存在了记忆体中
下面到了*  这里*代表 前面模式的0次或多次出现 虽然在功能上对于这个例子它等于([0-9]*\.)([0-9]*\.)([0-9]*\.)([0-9])/, 但是它并不等价于正则表达式/([0-9]*\.)([0-9]*\.)([0-9]*\.)([0-9])/ 。这时候仍然是模式2 去从左到右搜索$data剩余的字符串0.1  结果匹配了0.  由于仍然是模式2去匹配,所以0. 仍然存在记忆体中,冲掉了原来存在记忆体中的内容 下面([0-9])  匹配了1 并把它存在了记忆体中。

看到这里,可以试着总结出正则表达式的引擎搜索机制:
代码:
1  如果在正则表达式中有模式 ,那么每个模式从左到右搜索要匹配的字符串,如果匹配成功,则把匹配的字符串存到相对应的记忆体中。
2  如果正则表达式中有下一个模式,那么这个模式将继续从左到右搜索没有被匹配的字符串 ,重复前面的步骤1的过程,匹配成功则立即停止,并把匹配的字符串存到相应的记忆体中。
3  如果在2中的模式后面出现了数量符* + ? ,形如:(模式2)*,则匹配过程变为:模式2仍然按照步骤1的过程匹配,匹配成功则停止。但是由于这时候在模式2后面出现了数量符 * ,则仍然由模式2去匹配字符串中没有被匹配的的部分,如果这时候出现了可以匹配多个字符串的情况,那么作后一次匹配成功的字符串被存放到了模式2对应的记忆体中(注意:中间匹配成功的并不是没有存,而是被后面匹配成功的覆盖了)。

说到这里仿佛一切问题都得到了很好的解释
可是请看下面的例子
代码:
$data = 192.168.0.1
$data =~ /([0-9]*\.)([0-9]*\.)*([0-9]*\.)([0-9]*.)([0-9])/
按照上面的结论,大家不妨猜测一下的内容 ,也就是记忆体2中的值是什么
在没有看到答案之前,也许会有人猜测是0. 或者168.。
但是这里 的值是空值,*在这里是“0个”。

由此可以得到:正则表达式并不是按照模式按照从左到右搜索,"一个正则表达式" 就是一段程序,一个正则表达式的匹配引擎机制应该是:尽量多的精确的去匹配目标字符串,至于它采取哪种策略实现这一过程,这也是在此抛砖引玉的目的所在。

原文转自:http://www.ltesting.net