`
searun
  • 浏览: 174160 次
  • 性别: Icon_minigender_1
  • 来自: 合肥
社区版块
存档分类
最新评论

[跟我学正则表达式] 7. 使用子表达式

阅读更多

在前面的章节中演示过,元字符和字符匹配为正则表达式提供了基本的能力。在本章中将学习到使用子表达式如何进行分组。

 

理解子表达式

匹配多次出现的字符已经在第五章中进行了介绍。在那章中,“\d+”可以匹配一个或者多个数字,“https?://”可以匹配http:// 或 https://

在上面的例子中(实际上是现在遇到的所有例子),重复匹配元字符都是用在了前面的字符或者元字符上。例如, HTML 开发者在单词之间经常使用不间断空格来保证文本不会 wrap 。假设你需要定位所有重复的 HTML 不间断空格(并将其替换成其他字符),下面是个例子:

文本

Hello, my name is Ben Forta, and I am
the author of books on SQL, ColdFusion, WAP,
Windows  2000, and other subjects.

正则表达式
 {2,}

结果
Hello, my name is Ben Forta, and I am
the author of books on SQL, ColdFusion, WAP,
Windows  2000, and other subjects.

 

分析

“  " HTML 不间断空格的实体参考。模式“ {2,}”表示将匹配“  " 两次或者更多次。但是实际上并不是这样,为什么?“{2,}”实际上是指定了直接前面的字符的重复次数,在这里就是冒号,也就是说,“ ;;;;”将匹配,而“  ”并不会匹配。

 

子表达式的分组

上面的问题将我们带到了子表达式的主题。子表达式是一个大点的表达式,所有的部分组合起来作为一个实体。子表达式通过“ ( ”和“ ) ”括起来。

提示:“ ( ”和“ ) ”为元字符,如果要匹配这两个字符的话,需要使用转义“ \( ”和“ \) ”。

为了演示子表达式的用法,重新来看看上面的例子:

文本

Hello, my name is Ben Forta, and I am
the author of books on SQL, ColdFusion, WAP,
Windows  2000, and other subjects.

正则表达式
( ){2,}

结果
Hello, my name is Ben Forta, and I am
the author of books on SQL, ColdFusion, WAP,
Windows   2000, and other subjects.

分析

“( )”是一个子表达式并作为一个实体使用。在这里,“{2,}”将应用在整个子表达式上(而不仅仅是冒号)。这个模式很好的完成了工作。

 

下面是另外一个例子——用来定位 IP 地址的正则表达式。 IP 地址是由句号分隔的四组数字,如12.159.46.200等。因为每个部分的数字都可以为一个、两个或者三个数字字符,这个匹配模式可以表示为“\d{1,3}”。下面显示了这个例子:

文本

Pinging hog.forta.com [12.159.46.200]
with 32 bytes of data:

正则表达式
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}

结果
Pinging hog.forta.com [12.159.46.200 ]
with 32 bytes of data:
分析

每个\d{1,3}的实例都匹配了 IP 地址的一个数字。四个数字则是被“ \. ”表示的“ . ”分隔开的。

 

“\d{1,3}\.”模式(不超过三个的数字和“ . ”)重复了三遍,因此可以使用重复操作来处理。下面是相同例子的另外一个版本:

文本
Pinging hog.forta.com [12.159.46.200]
with 32 bytes of data:

正则表达式
(\d{1,3}\.){3}\d{1,3}

结果
Pinging hog.forta.com [12.159.46.200 ]
with 32 bytes of data:

分析

此模式可以和前面的一样工作,但是语法并不相同。表达式“\d{1,3}\.”使用“ \( ”和“ \) ”括起来从而组成子表达式。“(\d{1,3}\.){3}”重复了次子表达式三次(也就是 IP 地址的前三个数字),接着的“\d{1,3}”匹配最后的数字。

注意:“(\d{1,3}\.){4}”并不是一个定位 IP 地址的正确方法。知道为什么这个模式会在上面的例子中失败吗?

提示:有些用户喜欢将部分表达式括起来成子表达式来增加可读性。上面例子中的模式还可以写为“(\d{1,3}\.){3}(\d{1,3})”。这种实践是完全合法的,而且对正则表达式的行为没有影响(可能会有解析上性能的开销,这取决于正则表达式具体的实现)。

使用子表达式来分组是很重要的。让我们再来看一个例子,尽管这个例子中根本不包括重复。下面的正则表达式试图在用户记录中找到一个年份:

文本
ID: 042
SEX: M
DOB: 1967-08-17
Status: Active

正则表达式
19|20\d{2}

结果
ID: 042
SEX: M
DOB: 19 67-08-17
Status: Active

分析

在这个例子中,此模式用来定位一个包含四个数字的年份。为了增加精确度,前两个数字只能为 19 20 。在第三章中解释过,“ | ”是一个 OR 操作符,所以“19|20”匹配“ 19 ”或者“ 20 ”,所以模式“19|20\d2 ”匹配任意以“ 19 ”或者“ 20 ”开始的四个数字字符的年份。但是很显然,此模式并不能工作,为什么?“ | ”操作符从左到右读取,将“19|20\d{2}”分析为要么 19 ,要么“20\d{2}”(以 20 开头的四个数字字符)。换句话说,此模式将匹配数字 19 和所有 20 开头的四个数字字符。所以, 19 也将会匹配。

解决方法是将“ 19|20 ”作为一个子表达式,就像下面一样:

文本
ID: 042
SEX: M
DOB: 1967-08-17
Status: Active

正则表达式
(19|20)\d{2}

结果
ID: 042
SEX: M
DOB: 1967 -08-17
Status: Active

分析

使用了子表达式,“ | ”知道如何选择选项。“(19|20)\d{2}”将会正确的匹配 1967 ,因为此模式能够匹配所有 19 20 开头的四个数字字符。如果时间更靠后一点,如一百年以后,需要增加一个 21 的匹配,模式可以修改为(19|20|21)\d{2}。

  • 尽管本章使用了正则表达式的分组,在第八章中将介绍子表达式的一个很重要的用途。

 

嵌套子表达式

子表达式可以嵌套。实际上,子表达式可以一层一层嵌套在子表达式内。

嵌套子表达式的能力将极大的发挥正则表达式的威力,但是这将使得正则表达式看起来比较复杂,难以阅读和分析,有时候会有些吓人。事实上,嵌套子表达式由于其复杂性很少在实际中用到。

为了演示嵌套子表达式的用法,我们再来看看查找 IP 地址的例子。这是上面用过的模式(一个重复三次的子表达式和最后一个数字):
(\d{1,3}\.){3}\d{1,3}

那么这个模式有什么问题呢?语法上是没有的。每个 IP 地址确实是由四个数字组成,每个数字可以包含三个数字字符,并通过句号分隔。这个模式是正确的,将匹配所有的合法 IP 地址。但是这不是此模式唯一可匹配的,非法的 IP 地址也将被匹配。

一个 IP 地址由四个字节组成,所以如12.159.46.200的 IP 地址实际上是四个字节的表示。因此, IP 地址的四个数字就有单字节的范围, 0 255 。这意味着 IP 地址中的每个数字都是小于 255 的。而上面的模式可以匹配 345 700 甚至是 999 ,而这些实际上都是非法的 IP 地址。

笔记:这是一个很好的教训。写出一个符合需求的正则表达式是很容易的,但是要写出一个匹配所有期望并拒绝所有不期望的情况的正则表达式是很难的。

指定合法值的范围是可以的,但是正则表达式匹配字符而并不知道匹配的字符是什么意思。数学上的计算并不是一个选项。

是否有选项呢?可能。为了构建正则表达式,你必须清晰的定义需要匹配什么而不匹配什么。下面定义了所有合法 IP 地址所需要满足的一种条件:

  • 所有的一位数和两位数
  • 三位数的第一位为 1
  • 如果三位数的第一位为 2 ,且第二位从 0 4
  • 如果三位数的前二位为 25 ,且第三位从 0 5

当定义了需要匹配的情况后,就比较容易实现可以工作的模式。下面是个例子:

文本
Pinging hog.forta.com [12.159.46.200]
with 32 bytes of data:

正则表达式
(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))

结果
Pinging hog.forta.com [12.159.46.200 ]
with 32 bytes of data:

分析

这个模式明显可以工作,但是还是需要一些解释。使得此模式工作的原因是一系列的嵌套子表达式。首先从“(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.)”子表达式开始。包含了四个嵌套子表达式。“(\d{1,2})”可以匹配一位数和两位数,从 0 99 。“(1\d{2})”匹配了任何第一位为 1 的三位数,从 100 199 。“(2[0-4]\d) ”匹配数字从 200 249 。“(25[0-5])”匹配数字从 250 255 。每个子表达式都是通过“ | ”包括在另一个子表达式中(所以将匹配四个中的一种,而不是所有)。在数字范围之后是“ \. ”表示的“ . ”,然后这个系列括起来作为子表达式并重复三遍(使用“{3}”)。最后,数字范围被用来匹配最后一个 IP 地址的数字(没有了“ \. ”后缀)。由于将 IP 地址的四个数字都是限制在了 0 255 之间,所以此模式可以匹配所有的合法 IP 地址,而拒绝所有不合法的 IP 地址。

 

提示:像这样的正则表达式看起来很复杂。理解其的关键是分解、分析和理解每个子表达式。试着从子表达式开始从内到外,而不是从头开始一个一个字符解析。这将使得正则表达式看起来没有那么复杂。

 

小结

子表达式通过“ ( ”和“ ) ”定义,并用于对表达式进行分组。子表达式的一般用法是精确的控制重复的对象和 OR 条件的使用。如果需要的话,子表达式可以嵌套。

 

@ 亦歌亦行 http://searun.iteye.com

分享到:
评论

相关推荐

    正则表达式系统教程 chm

     正则表达式的使用,可以通过简单的办法来实现强大的功能。为了简单有效而又不失强大,造成了正则表达式代码的难度较大,学习起来也不是很容易,所以需要付出一些努力才行,入门之后参照一定的参考,使用起来还是...

    正则表达式30分钟入门教程

    最重要的是——请给我30分钟,如果你没有使用正则表达式的经验,请不要试图在30秒内入门——除非你是超人 :) 别被下面那些复杂的表达式吓倒,只要跟着我一步一步来,你会发现正则表达式其实并没有你想像中的那么困难...

    正则表达式

     正则表达式的使用,可以通过简单的办法来实现强大的功能。为了简单有效而又不失强大,造成了正则表达式代码的难度较大,学习起来也不是很容易,所以需要付出一些努力才行,入门之后参照一定的参考,使用起来还是...

    实例详解C#正则表达式

    有一段时间,正则表达式学习很火热很潮流,当时在脚本之间平台一天就能看到好几个正则表达式的帖子,那段时间借助论坛以及Wrox Press出版的《C#字符串和正则表达式参考手册》学习了一些基础的知识,同时也为我在CSDN...

    常用C#正则表达式汇总介绍

    有一段时间,正则表达式学习很火热很潮流,当时在CSDN一天就能看到好几个正则表达式的帖子,那段时间借助论坛以及Wrox Press出版的《C#字符串和正则表达式参考手册》学习了一些基础的知识,同时也为我在CSDN大概赚了...

    Python爬虫 Re库与正则表达式的细节解析

    上一篇中我们已经提到了,正则表达式使用 ‘’ 字符来使得一些普通的字符拥有特殊的能力(例如 \d表示匹配任何十进制数字),或者剥夺一些特殊字符的能力(例如 [ 表示匹配左方括号 ‘[’)。这会跟 Python字符串中...

    正则表达式 口诀 学习正则的朋友看看

    又受五笔字型字根表口诀“白手看头三二斤…”的启发, 试作“正则表达式助记口诀”又名“正则打油诗”,版本0.1,绝对原创,仿冒必究,:) 注:本文仅为学习正则时为了便于记忆而作,不能代替系统而全面的学习过程...

    C# 正则表达式经典分类整理集合手册第1/3页

    有一段时间,正则表达式学习很火热很潮流,当时在CSDN一天就能看到好几个正则表达式的帖子,那段时间借助论坛以及Wrox Press出版的《C#字符串和正则表达式参考手册》学习了一些基础的知识,同时也为我在CSDN大概赚了...

    Java-PHP-C#

    此外,JavaScript这种客户端的脚本语言也提供了对正则表达式的支持,现在正则表达式已经成为了一个通用的概念和工具,被各类技术人员所广泛使用。 在某个Linux网站上面有这样的话:"如果你问一下Linux爱好者最喜欢...

    Java正则相关的Pattern和Matcher类及遇到的坑

    此篇文章是记录我在学习Java正则表达式时候学到的和遇到的坑。 先来说说 Matcher 里面的三个方法(取的结果以group()方法为例子) matches():整个匹配,只有整个字符序列完全匹配成功,才返回True,否则返回False...

    你必须知道的495个C语言问题(PDF)

    3.10 如果我不使用表达式的值, 我应该用++i 或i++ 来自增一个变量 吗? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.11 为什么如下的代码int a = 100, b = 100; long int c = a * b;...

    MATLABMYSQL爬虫-zhgd.m

    分析:每张图片都为 bimg 图片,作为正则表达式的筛选对象。注意:每种产品不一定是一张图片,需要对多张图片进行考虑 iii.简介:见后面图片 分析: 简介末尾都有 <!--/商品属性--> ,作为正则表达式的...

    Linux使用教程(教程)

    grep正则表达式元字符集(基本集) 34 ※8.cut and paste 35 ※9.文件合并jion 37 ※10.文件分割split 38 ※11.文件的压缩 38 ※1.gzip 、zcat命令 39 ※2.bzip2、bzcat 39 ※3.tar (打包文件或目录) 40 ※12....

    .C#常用类库(多年积累)

    跟师傅学编程快一年了,一年来师傅...有字符串操作,XML操作类,常用网络解析类,正则表达式,验证码,解压缩,文件操作等等60余个类文件动态库文件哦。其实有好多类我都还没来的及去用,希望用过的朋友能提宝贵意见

    DotNet C# 常用类库(多年积累)

    跟师傅学编程快一年了,一年来师傅...有字符串操作,XML操作类,常用网络解析类,正则表达式,验证码,解压缩,文件操作等等60余个类文件动态库文件哦。其实有好多类我都还没来的及去用,希望用过的朋友能提宝贵意见

    使用Matlab下载googlefinance上面的option数据版本2012a-getoption.m

    这个东西是叫做matlab正则表达式的应用,想系统学的同学可以上网搜,比我讲的要好, 我只是要用的时候拿出来看, 上图:   大家看到strike的规律没有? 都是strike 后面跟着一个价格, 所以,下面这个code就是讲得是...

    如何完全使用python实现工作

    ”为什么满屏的正则表达式?“!   7.C#,php,javascript:呵呵。   8.Shell:这算语言么?   9.Matlab:第一,我穷酸学生没钱每年买你的正版,看到激活码就想吐。第二,我不想心血来潮...

    万能makefile写法详解,一步一步写一个实用的makefile

    该命令表示把源串内的match都替换成replace,s指示match可以是正则表达式。 g表示把每行内所有match都替换,如果去掉g,则只有每行的第1处match被替换(实际上不需要g,因为一个.d文件中,只会在开头有一个main.o:)。...

    C语言FAQ 常见问题列表

    o 7.7 有人跟我讲, 数组不过是常指针。 o 7.8 我遇到一些 ``搞笑" 的代码, 包含 5["abcdef"] 这样的 ``表达式"。 这为什么是合法的 C 表达式呢 ? o 7.9 既然数组引用会蜕化为指针, 如果 arr 是数组, 那么 arr 和...

    asp.net知识库

    .net中的正则表达式使用高级技巧 (一) C#静态成员和方法的学习小结 C#中结构与类的区别 C#中 const 和 readonly 的区别 利用自定义属性,定义枚举值的详细文本 Web标准和ASP.NET - 第一部分 XHTML介绍 在ASP.NET...

Global site tag (gtag.js) - Google Analytics