加载中…
个人资料
lanael_631
lanael_631
  • 博客等级:
  • 博客积分:0
  • 博客访问:0
  • 关注人气:0
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

正则表达式初级教程精讲(六):分组和向后引用

(2014-03-25 06:55:28)
标签:

it

正则表达式

字表达式

向后引用

分组

分类: 程序猿

分组:

 

前面我们说了重复符,还举过一个例子,用“Wind\d+”来匹配带数字的微软视窗操系版本。这里“+”限定了它前面一个元字符“\d”的重复。但如果我们想要重复的部分不是“\d”,而是“Wind\d”呢?

 

( ) 被包括于其中的内容将分成一个组。被分组的部分又称为子表达式

 

所以把“Wind\d”用圆括号括起来,变成 (Wind\d)+ 就符合要求了。是不是觉得以前的例子都太简单?我们来个拿得出手的例子,顺便总结一下到目前为止学过的主要知识:

 

百度百科里,对于IP地址的描述是这样的:“(IP地址)分为4段,……用十进制数字表示,每段数字范围为0~255,段与段之间用句点隔开。例如159.226.1.1……”。除此之外,不满3位的IP地址段里可以使用前导0 (leading zeros)来补,比如“.02”或“.002”也是合法的IP地址段,它们和“.2”是一样的。

 

OK,我们来为它编写正则表达式!

 

先看条件“每段数字范围为0~255”,这是最核心的。怎么写这条表达式?同样答案不止一条,大家可以自己想。这里给出其中一种分析:

  • 当IP段为3位数且百位为2时比较特殊,因为它的十位数最高只能到5,且当十位是5时个位最高也只能是5。所以我们把百位为2的情况分出来单独讨论;
  • 当IP段范围落在000到199之间时,
  1. 个位可以是0~9中的任一数字。表达式为[0-9],也就是\d
  2. 十位为0~9中的任一数字,或不存在(先导0通常省略不写)。表达式为\d?
  3. 百位为0或1,或不存在(先导0通常省略不写)。表达式为[01]?
  • 那么000到199之间的IP段我们可以写成:[01]?\d?\d
  • 回过头来看看IP段百位为2的情况,首先表达式第一个字符是2,这点毋庸置疑。接着分析,
  1. 因为当十位是5时个位最多只能是5,这是一个特殊情况,我们需要单独把它表达出来:25[0-5]
  2. 当十位落在0~4之间时,个位数没有限制,0~9都可以取值,所以表达式是:2[0-4]\d
  • 因而百位为2的时候IP段的正则表达式可以写成:25[0-5]|2[0-4]\d

综上所述,单个IP地址段的正则表达式是:

25[0-5]|2[0-4]\d|[01]?\d?\d

好了,接着我们看,如果用未知数X表示IP地址,可以写成:XXXX . XXXX . XXXX . XXXX。根据完全统计,一个完整的IP地址总共有三个点“.”,总结规律就是,前三个IP段后面都跟着一个点,最后一个IP段就不跟着点了。

 

所以我们可以运用这节刚讲的知识,把单个IP段的正则表达式再加上一个点作为一个组,重复三次,再加上一个未加点的单IP段正则表达式,就完成任务了:

 

(25[0-5]|2[0-4]\d|[01]?\d?\d\.){3}(25[0-5]|2[0-4]\d|[01]?\d?\d)

 

总结一下这个例子,我们使用的知识有:

出现0次或1次的限定重复符?;范围集合符[ ];代表数字的元字符\d并且知道了它等价于[0-9];逻辑“或”符 |;转义符 \;精确重复符{ };最后是本节新接触的知识——分组符( )

 

向后引用:

 

我们刚刚学过分组,配合重复符的使用,可以大大缩短正则表达式的长度。除此之外,分组还有另一个简化正则表达式编写的语法,就是这里要讲的向后引用

 

正则表达式在从左到右扫描时,会以碰到左括号的先后为序,给每个组分配组号,用转义符加数字表示。比如第一组的组号为“\1”,然后是“\2”,“\3”……以此类推。有了组号以后,该组所捕获到的与之匹配的文本内容可以在后面的表达式直接用组号来调用。这种功能,称作向后引用

 

注意上面描述向后引用的定义时用的两个关键词:捕获、文本、调用。我们给出个表达式,再举实例看一下:

(\w+!?)\s\1\s\1

这个表达式匹配什么?

 

还记得98年世界杯的主题歌曲吗(记不得?恭喜你,你很年轻!),里面有两句歌词被祥林嫂式地反复吟唱:

Goal! Goal! Goal!

Ole ole ole!

歌词里的绿色部分就是本例的表达式 (\w+!?)\s\1\s\1 所匹配的结果(注意,最后那个感叹号不是绿色,不匹配)。分析一下:

  • (\w+!?) 匹配的是一个单词,后面可以跟一个感叹号,也可以不跟。然后把他们分作一个组。计算机扫描这个表达式时,就分配“\1”来表示这个组。
  • 观察一下这两句歌词的文本内容,可以发现“Goal!”和“Ole”都是这个分组匹配词。
  • 计算机当然不会比我们笨,我们能看出来这两个匹配词,计算机自然也捕获到了。
  • 于是“Goal!”和“Ole”以为本的方式,成为“\1”的内容,随时恭候调用。
  • \s\1\s\1 表示分组后面紧跟着一个空格,然后是对“\1”的调用。紧接着又是一个空格加一个调用。所以,当 (\w+!?) 匹配的是“Goal!”的时候,整个表达式匹配的是:“Goal! Goal! Goal!”;当(\w+!?) 匹配的是“Ole”时就是“Ole ole ole”。
  • 用类似的办法,可以很轻松的将一个文本中连续重复出现的单词匹配出来。

 

这就是向后引用的工作方式。用好了它,同样可以大大的简化正则表达式的编写。当然,我们还可以给一些重要的分组命名;又或者某个分组的位置比较靠后,我们懒得去数它到底是第几组,那么就直接给它命名以便调用。

 

给分组命名的语法是:(?<</B>组名>表达式)(?’组名表达式)

如果你觉得汉字太土,那就写成:(?Exp)(?’name’Exp)

然后再用 \k<</B>组名>\k’ 组名 来调用这个组捕获的文本内容。

 

现在,假设我们想给上例中的 (\w+!?) 起个名叫“IBM”,而不想让计算机把它分配为“\1”,我们可以用(?\w+!?) 来重新命名。那么整个表达式就写成:

(?\w+!?)\s\k\s\k

 

到目前为止,可能你开始对这些代码烦躁了。还有更烦躁的,不过还好,教程也快结束了,坚持……

 

本教程所有匹配示例均默认忽略英文大小写。

 

作者:lanael,转载请注明出处。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有