首页 > 学技术 > 技术网文 > Linux Shell > 正文

[保留] 正则表达式的细节


来源 chinaunix.net 酷勤网整理

在搞清楚所述及的问题之前
首先明确一下什么是匹配
匹配可以从两个方面来理解
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])/;
$1 is 192.
$2 is 168.
$3 is 0.
$4 is 1
这里
/([0-9]*\.)([0-9]*\.)([0-9]*\.)([0-9])/  匹配了192.168.0.1
同时在这个较长的正则表达式子是由几个短的正则表达式组成的,这些短的在()中的正则表达式称为模式,大家对$1,$2,$3,$4的值的输出都非常理解

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

$1 is 192.
$2 is 0.
$3 is 1

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


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

$1 is 192.
$2 is 168.
$3 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  存在正则表达式的记忆体 \1 \2 \3中 
而对于 $data =~ /([0-9]*\.)([0-9]*\.)*([0-9])/;
对于模式1  从左到右搜索$data 匹配了192.    把它存在了记忆体  \1中
对于模式2  从左到右搜索剩下的$data 部分  匹配了 168.  并把它存在了记忆体\2中
下面到了*  这里*代表 前面模式的0次或多次出现 虽然在功能上对于这个例子它等于([0-9]*\.)([0-9]*\.)([0-9]*\.)([0-9])/, 但是它并不等价于正则表达式/([0-9]*\.)([0-9]*\.)([0-9]*\.)([0-9])/ 。这时候仍然是模式2 去从左到右搜索$data剩余的字符串0.1  结果匹配了0.  由于仍然是模式2去匹配,所以0. 仍然存在记忆体\2中,冲掉了原来存在记忆体\2中的内容 下面([0-9])  匹配了1 并把它存在了记忆体\3中。

看到这里,可以试着总结出正则表达式的引擎搜索机制:
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的内容 ,也就是记忆体2中的值是什么
在没有看到答案之前,也许会有人猜测是0. 或者168.。
但是这里 \2的值是空值,*在这里是“0个”。

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



 honbj 回复于:2005-09-05 09:19:17

望高手们出手指正


 3x3eyes 回复于:2005-09-05 12:56:08

一不小心成了反面典型
不过,楼主,我还是认为shell是把所有模式里的RE全都解释了以后,用这整个RE去匹配的

很感激楼主的分析与讲解


 creat7151 回复于:2005-09-05 15:08:03

好贴,收下


 yuyuyou 回复于:2005-09-12 23:49:53

顶下


 寂寞烈火 回复于:2005-09-13 00:29:43

眼晕呀~~~~  :em19:  :em19:  :em19:


 gongjijiao 回复于:2005-09-16 09:49:12

中文匹配该怎么办?




原文链接:http://bbs.chinaunix.net/viewthread.php?tid=605791
转载请注明作者名及原文出处



收藏本页到: