IT技术博客大学习 共学习 共进步

正则表达式傻瓜书 第二章:元字符

乱象,印迹 2010-04-28 15:39:12 浏览 4,104 次

上一章,我们通过Word中的“使用通配符”模式,粗略见识了正则表达式的使用方法。然而通配符并不等于正则表达式,遇到复杂的情况,通配符就力不从心了。所以从本章开始,我们来看“正宗”的正则表达式。

安装Regular Expression Tester

“工欲善其事,必先利其器”,学习正则表达式也是如此。尽管正则表达式的思想和规则是基本确定的,应用起来却有许多讲究(比如,在Java、C++、Python等不同的编程语言中,同一个表达式的具体写法是不同的,在Word、Excel等软件中也是这样)。所以,学习正则表达式最好采用“中立而规范”的工具――这有点像学习摄影,开始应该学习的是构图、用光,而不是尼康、佳能或索尼相机的特性。

本书中,我们采用Firefox的插件Regular Expression Tester(意思是“正则表达式测试工具”)来学习和讲解正则表达式,选择它的好处在于:不需要搭建编程语言环境(许多时候我们并不需要在编程语言中应用正则表达式);在Windows/Unix/Mac上都可以使用;并且支持大多数通用的正则表达式功能。如果你没有接触过它,也不用担心,下面我们介绍它的安装和使用。

Regular Expression Tester是Firefox(火狐)浏览器的一个插件,因此,如果你没有安装Firefox浏览器,在使用这个插件之前,必须首先安装Firefox浏览器:

进入网址http://www.mozillaonline.com/ ,选择自己需要的版本,点击下载,之后运行下载的程序,完成安装。

图2-1,下载Firefox浏览器

然后,启动Firefox浏览器,选择工具―扩展[xdx1] (在某些版本的Firefox中,这里也可能是“附加组件”),在“获取附加组件”中输入“regular expression tester”,即可搜索到该插件,请点击右侧的“添加至Firefox”按钮,稍后Firefox会提醒尚未验证该插件的作者,没有关系,请选择“立即安装”。安装完毕重启Firefox就可以使用“regular expression tester”插件了。

图2-2,安装Regular Expression Tester

使用Regular Expression Tester

在Firefox的菜单栏选择“工具――扩展”,下拉菜单中选择Regular Expression Tester,就可以看到Regular Expression Tester的主界面。 我们可以看到,这个工具分为三大块,最上面的Regular Expression文本框用于输入正则表达式,中间的Search Text文本框用于输入待匹配的文本,下面的Result则显示匹配(还包括替换,将来我们会看到)的结果,左下角显示正则表达式操作进行的时间,右下角显示匹配发生的次数。

图2-3,Regular Expression Tester的界面

在Regular Expression和Search Text之间,还有几个选项:Case sensitive、Global、Multiline,它们对应到非常实用的功能,后面我们会详细介绍它们的意义和用途,但现在请只勾选Global选项。


图2-4,现在只需要勾选Global

先测试上一章中Word中查找手机号码的例子,把手机号输入Search Text文本框中,每行一个号码,然后在Regular Expression文本框中输入表达式『1310000[0123456789][0123456789][0123456789][0123456789].doc』 ,Result中迅速出现了结果(以黄色高亮标明),表达式匹配成功。

图2-5,『1310000[0123456789][0123456789][0123456789][0123456789].doc』的匹配

如果你仔细观察,会发现Result中每一行的末尾都有一个特殊的符号 ――没错,正则表达式处理的是字符,而每一行末尾的回车/换行符,虽然“看不见”,也是一个字符,为了展示文本的“真实面貌”,Regular Expression Tester用这个符号标识。或许你现在觉得有些别扭,但是请相信我,下面你将会发现这样的好处。

*和?的新含义

接下来,我们测试上一章中的另一个表达式『1310000????.doc』,你一定记得,在勾选了“使用通配符”之后,它就能“大致”匹配1310000号段的手机号码(虽然不一定准确)。现在我们用Regular Expression Tester重复验证一次。

图2-6,『1310000????.doc』匹配出错

出错了!匹配结果栏显示“The entered string is not a regular expression(输入的字符串不是正则表达式)”!问号作为通配符是可以匹配一个任意字符的,那么问题究竟出在哪里呢?

其实,我们已经提到过原因:通配符不等于正则表达式!通配符中*表示“任意字符串”,?表示任意字符,但这只是通配符中的规定,不是正则表达式中的规定!

正则表达式中的*和?

在通配符的世界里,〖*〗表示“任意字符串” ,〖?〗表示“任意字符”(这里的“任意长度字符串”和“单个任意字符”都是为了保持与之前一致,你看是否有全部修改的必要) ,那么,正则表达式中如何表示这两种意义呢?要弄清这个问题,我们先看看正则表达式中的『*』和『?』表示什么意义。 『*』和『?』在正则表达式中都叫做“量词”(英文名叫quantifier),它用来“度量”字符串的长度,也就是字符能够出现的次数,其中『*』表示字符出现次数可以从0到无穷多(也就是“任意长度”了),而『?』表示字符出现0次或者1次。所以,正则表达式『6*』能匹配的,就是空字符串””,或者是”6”、”66”、”66666”……这样的字符串,而正则表达式『6?』能匹配的,只能是空字符串””,或者是单个字符构成的字符串 6

图2-7,『6*』的匹配

注意:regular expression tester”目前无法显示空字符串,所以无法演示匹配空字符串的情况;本例中输入”666666”,你可以自己尝试,无论字符串中包含多少个6,都能匹配。

图2-8,『6?』只能匹配字符串”6”,而不能匹配”66”,”666”

请注意,量词只关心字符出现的次数,与字符没有任何关系:『7*』能匹配空字符串””,”7”,”77”,”777”…,『8*』能匹配空字符串””,”8”,”88”,”888”…。要模拟通配符*,还必须规定每个字符可以是“任意字符”,所以需要有办法表示“任意字符”。好在正则表达式中存在一个特殊符号,也就是普通的点号(英文句号)『.』,它表示“任意字符”,把“任意字符”和“任意长度”组合起来,就得到了“任意字符串”,也就是通配符*的意义了(这里能否麻烦你做个图,我希望是类似一个直角坐标系的,一个原点,两个带单方向箭头的轴,纵向标明“任意字符”,横向标明“任意长度”,图的名称就是“任意字符串”)。

图2-9,『.*』的匹配

现在你多半已经想到了,通配符*表示“任意字符串”,对应的正则表达式是『.*』,那么通配符?表示“任意字符”,恰好就对应了正则表达式中的『.』。因此,在表达式『1310000[0123456789][0123456789][0123456789][0123456789].doc』中,点号『.』其实只是恰好匹配了字符串中的”.”而已,如果此处出现其它字符,也可以匹配。

图2-10,表达式中的『.』“恰好”匹配了【.】

现在我们已经知道了,通配符*和?的功能,在正则表达式中要如何实现。

元字符和转义

前面我们提到了,通配符*、?具有特殊含义,所以不能出现在文件名中。同样具有特殊含义的正则表达式字符『.』、『*』和『?』,是否应该禁止出现在正则表达式要处理的普通文本中?这显然不可能――这几个符号都是很常见的,如果使用正则表达式就不容许它们出现,正则表达式所能处理的文本就非常有限。可是,文本中的*、?等等“特殊符号”要如何来准确匹配呢?

图2-11,正则表达式中的『.』

『.』能匹配任意字符,怎么准确匹配字符串中的”.”? 其实,所谓“特殊符号”就是“意义不同于字符本身的字符”:字符『6』不是“特殊字符”,因为它只能表示字符”6”;相反,字符『*』表示“字符出现次数可以从0到无穷多”,点号『.』表示“任意字符”。在正则表达式中,这些具有特殊意义的“特殊字符”有个统一的名字“元字符(meta-character)”――它的意思是“具有特殊意义的字符”,不管你看它顺不顺眼,记住这个名字就好了。 其实,“元字符”本身的匹配非常简单,只有一条规则:转义。如果某个字符在正则表达式中具有特殊的意义(上面我们提到了『.』和『*』,将来还会遇到更复杂的情况),要表示这个字符本身,就在前面添加反斜线 \ 即可。

图2-12,用『\.』匹配点号

元字符经过转义,就失去了特殊意义,变成了普通字符,所以正则表达式『.\*』的意思就是“先匹配一个任意字符,再匹配一个星号*”。


图2-13,用『.\*』的匹配

如果你觉得转义的概念有些麻烦,不妨这样想:转义只是为了避免混淆的一种形式变化,转义序列\*“看起来”像两个字符构成的字符串,然而从概念上来说,它只表示一个普通字符*。我们要记住的是概念,而不是形式

补充内容:编程语言中的转义

编程中的转义比较特别,值得拿出来讲一讲。

在一般的编程语言中,正则表达式都是以字符串(String)构造的。而对于字符串这种数据类型,多数编程语言都规定了一些转义字符序列来表示特殊字符(注意,这里说的是编程语言中的特殊字符,而不是正则表达式中的“元字符”),比如〖\n〗表示换行符,〖\t〗表示制表符。因此,在编程语言中使用正则表达式时,转义是一个麻烦的问题,但是,我们又必须弄清楚这个问题――网络上时常看到有人提问“这个正则表达式里到底需要几个反斜线?”。

转义的问题其实非常简单:在编程语言中,作为字符串出现的正则表达式,必须经过“字符串的转义”,“正则表达式的转义”才能真正生效。

举例来说,我们的正则表达式里需要出现『\*』,也就是“带转义(去掉了特殊意义)的〖*〗字符”。因为正则表达式是以字符串形式表现的,如果直接在字符串里写 \* ,许多编程语言就会报错:因为它只认识 \t 、 \n 之类的转义序列,不认识 \* 。

解决的办法是在 \* 之前添加反斜线 \ ,写成 \\* 。这样,编程语言在处理字符串时,会首先将 \\ “翻译”成 \ ,正则表达式得到的就是 \* 了。

以上说的情况比较简单,可是,如果我们需要在正则表达式中使用反斜线字符『\』呢?这个字符首先必须以转义形式写出,也就是说,在正则表达式中必须写成『\\』;但每个反斜线,在字符串里又需要转义,所以正则表达式中的一个『\』,在字符串里就必须写成【\\\\】!

不过,也有些语言不会报错――它们遇到“未定义”的转义字符,会直接忽略反斜线的转义功能,将它视为普通字符序列,Python,PHP都是如此(注:JS是否如此尚未验证)。

我的建议是,在对元字符转义时,心里一定要明白应该写几个反斜线;同时,推荐你在使用Python和PHP时,不要省略一个反斜线字符。

小结

在本章,我们真正踏入了正则表达式的殿堂。安装好Regular Expression Tester,就搭建好了学习正则表达式的舞台。

我们也知道了,通配符不同于正则表达式,在正则表达式中,*表示字符出现次数可以从零到无穷多,?表示表示字符出现零次或者一次,它们都是量词,属于元字符。通配符*就等于正则表达式『.*』,通配符?就等于正则表达式『.』。

通配符 正则表达式
? .
* .*

最后要记得的是,如果需要取消元字符的特殊意义,必须使用反斜线进行转义,在一些编程语言中,可能需要两个反斜线字符――我们推荐这么做!

建议继续学习

  1. grep 正则表达式选项要记得转义 (阅读 6,445)
  2. 统计最近用过的linux命令 (阅读 6,405)
  3. 正则表达式基础 (阅读 6,162)
  4. 正则表达式的与或非 (阅读 5,744)
  5. 学习Grep,Sed中的正则 (阅读 5,267)
  6. URL正则表达式 (阅读 4,663)
  7. 字符引用和空白字符 (阅读 4,624)
  8. 正则表达式简要入门 (阅读 4,365)
  9. 正则转义符汇总 (阅读 4,321)
  10. 使用Oracle正则表达式监控应用到数据库的连接情况 (阅读 4,267)