《精通正则表达式-基础知识拓展》由会员分享,可在线阅读,更多相关《精通正则表达式-基础知识拓展(8页珍藏版)》请在金锄头文库上搜索。
1、精通正则表达式_基础知识拓展2007-12-22 13:37基础知识拓展Expanding the Foundation我希望,前面的例子和解释已经帮助读者牢固地打下了正则表达式的基础,也请读者明白, 这些例子都很浅显,我们需要掌握的还有很多。语言的差异Linguistic Diversification我已经介绍过大多数版本的 egrep 支持的正则表达式的特性,这样的特性还有很多,其中一 些并不是所有的版本都支持,这个问题留到后面的章节讲解。任何语言中都存在不同的方言和口音,很不幸,正则表达式也一样。情况似乎是,每一种支 持正则表达式的语言都提供了自己的“改进”。正则表达式不断发展,但多年
2、的变化也造就了 数目众多的正则表达式“流派”(flavor) 。我们会在下面的章节中见到各种例子。正则表达式的目标The Goal of a Regular Expression从最宏观的角度看,一个正则表达式要么能够匹配给定文本(对 egrep 来说,就是一行文本) 中的某些字符,要么不能匹配。在编写正则表达式的时候,我们必须进行权衡:匹配符合要 求的文本,同时忽略不符合要求的文本。尽管 egrep 不关心匹配文本在行中的位置,但对正则表达式的其他应用来说,这个问题却很 重要。如果文本是这样:zip is 44272. If you write, send $4.95 to cover po
3、stage and我们只希望找出包含0-9+的那些行,就不需要关心真正匹配的数字。相反,如果我们需 要操作这些数字(例如保存到文件、添加、替换之类我们会在下一章看到这样的处理) , 就需要关心确切匹配的那些数字。更多的例子A Few More Examples在任何语言中,经验都是非常重要的,所以我会给出更多用正则表达式匹配常用文本结构的 例子。编写正则表达式时,按照预期获得成功的匹配要花去一半的工夫,另一半的工夫用来考虑如 何忽略那些不符合要求的文本。在实践中,这两方面都非常重要,但是目前我们只关注“获 得成功匹配”的方面。即使我没有对这些例子进行最全面彻底的解释,它们仍然能够提供有 用的启
4、示。 变量名许多程序设计语言都有标识符(identifier,例如变量名)的概念,标识符只包含字母、数字 以及下画线,但不能以数字开头。我们可以用a-zA-Z_a-zA-Z_0-9*来匹配标识符。第 一个字符组匹配可能出现的第一个字符,第二个(包括对应的*)匹配余下的字符。如果 标识符的长度有限制,例如最长只能是32个字符,又能使用第20页介绍的区间量词min, max,我们可以用0,31来替代最后的*。引号内的字符串匹配引号内的字符串最简单的办法是使用这个表达式:“*“。两端的引号用来匹配字符串开头和结尾的引号。在这两个引号之间的文本可以包括双引号之 外的任何字符。所以我们用“来匹配除双引号
5、之外的任何字符,用*来表示两个引号 之间可以存在任意数目的非双引号字符。关于引号字符串,更有用(也更复杂)的定义是,两端的双引号之间可以出现由反斜线转义 的双引号,例如“nailthe2“x4“plank“。在后面的章节讲解匹配实际进行的细节时,我们会 多次遇到这个例子。美元金额(可能包含小数)$0-9+(.0-90-9)?是一种匹配美元金额的办法。从整体上看,这个表达式很简单,分为三部分:$、+和()?,可以大致理解为: 一个美元符号,然后是一组字符,最后可能还有另一组字符。这里的“字符”指的是数字(一 组数字构成一个数值) , “另一组字符”是由一个小数点和两位数字构成的。从几个方面来看,
6、这个表达式还很简陋。比如,它只能接受$1000,而无法接受$1,000。它 确实能接受可能出现的小数部分,但对于 egrep 来说意义不大。因为 egrep 从不关心匹配文 字的内容,而只关心是否存在匹配。处理可能出现的小数部分对整个表达式能否匹配并没有 影响。但是,如果我们需要找到只包含价格而不含其他字符的行,倒是可以在这个表达式两端加上$。这样一来,可选的小数部分就变得很重要了,因为在金额数值和换行符之间是否存 在小数部分,决定了整个表达式的匹配结果是否存在差异。另外,这个正则表达式还无法匹配$.49。你可能认为把加号换成星号能够解决问题,不过 这条路走不通。在这我先卖个关子,答案留待第5
7、章(F194)揭晓。HTTP/HTML URLWeb URL 的形式可能有很多种,所以构造一个能够匹配所有形式的 URL 的正则表达式颇有 难度。不过,稍微降低一点要求的话,我们能够用一个相当简单的正则表达式来匹配大多数 常见的 URL。进行这种检索的原因之一是,我只能大概记得在收到的某封邮件中有一个 URL 地址,不过一见到它我就能认出来。常见的 HTTP/HTML URL 是下面这样的:http:/hostname/path.html当然,.htm 的结尾也很常见。hostname(主机名,例如 )的规则比较复杂,但是我们知道,跟在http:/之 后的就有可能是主机名,所以这个正则表达式就
8、很简单,-a-z0-9_.+。path 部分的变化更 多,所以我们需要使用-a-z0-9_:&?=+,.!/*%$*。请注意,连字符必须放在字符组的开 头,保证它是一个普通字符,而不是用来表示范围(F9) 。综合起来,我们第一次尝试的正则表达式就是:% egrep i files因为我们降低了对匹配的要求,所以http:/./foo.html也能匹配,虽然它显然不是一个合法 的 URL。我们需要关心这一点吗?这取决于具体的情况。如果我只是需要扫描自己的 E- mail,得到一些错误结果并不算是问题。而且,我没准会用更简单的表达式:% egrep i files在深入了解如何调校正则表达式之后,
9、读者会明白,要想在复杂性和完整性之间求得平衡,一个重要的因素是了解待搜索的文本。下一章,我们会更详细地考察这个例子。HTML tag对 egrep 这样的工具来说,简单地匹配包含 HTML tag 的行并不常见,也没什么用。但是, 探索如何准确匹配一个 HTML tag 却是相当有启发的,在下一章我们深入考察更高级的工具 时,这一点尤其明显。简单的例子包括和,我们可能会想到。这个简单的表达式往往是最 直接的想法,但它显然是不对的。的意思是, “先匹配一个”。所以,它无疑能够匹配不止一个 tag 的内容,例如this short example中标记的内容。也许结果有点出乎你的意料,但是我们目前
10、还只在第1章,对正则表达式的理解也不够深入。 我之所以举这个例子,是想说明正则表达式并不复杂,但是如果你不真正弄懂它们,可能会 被搞得晕头转向。在下面的几章中,我们会学习理解和解决这个问题需要的所有细节。表示时刻的文字,例如“9:17 am”或者“12:30 pm”匹配表示时刻的文字可能有不同的严格程度。0-9?0-9:0-90-9(am|pm)能够匹配9:17am 或者12:30pm,但也能匹配无意义的时刻,如99:99pm。首先看小时数,我们知道,如果小时数是一个两位数,第一位只能是1。但是1?0-9 仍然 能够匹配19(也能够匹配0) ,所以更好的办法应该是把小时部分分为两种情况来处理,
11、1012 匹配两位数,1-9匹配一位数,结果就是(1012|1-9)。分钟数就简单些。第一位数字应该是0-5,此时第二位数字应该是0-9。综合起来就是(1012|1-9):0-50-9(am|pm)。举一反三,你能够处理24小时制的时间吗?多动动脑筋,想想该如何处理以0开头的情况, 比如09:59呢?v 答案请见下页。正则表达式术语汇总Regular Expression Nomenclature正则(regex)你或许已经猜到了, “正则表达式”(regular expression)这个全名念起来有点麻烦,写出来就 更麻烦。所以,我一般会采用“正则”(regex)的说法。这个单词念起来很流
12、畅(有点像联邦 快递的 FedEx,与 regular 一样,g 发重音,而不同于 Regina) ,而且说“如果你写一个正则”, “巧妙的正则”(budding regexers) ,甚至是“正则化”(regexification) (注10) (译注4) 。匹配(matching)一个正则表达式“匹配”一个字符串,其实是指这个正则表达式能在字符串中找到匹配文本。 严格地说,正则表达式a不能匹配 cat,但是能匹配 cat 中的 a。几乎没人会混淆这两个概 念,但提一提还是有必要的。元字符(metacharacter)一个字符是否元字符(或者是“元字符序列”(metasequence) ,这
13、两个概念是相等的) ,取决 于应用的具体情况。例如,只有在字符组外部并且是在未转义的情况下,*才是一个元字符。 “转义”(escaped)的意思是,通常情况下在这个字符之前有一个反斜线。*是对*的转义,而*则不是(第一个反斜线用来转义第二个反斜线) ,虽然在两个例子中,星号之 前都有一个反斜线。正则表达式的流派(flavor)不同,关于字符转义的规定也不相同。第3章对此进行了详细讨 论。流派(flavor)我已经说过,不同的工具使用不同的正则表达式完成不同的任务,每样工具支持的元字符和 其他特性各有不同。我们再举单词分界符的例子。某些版本的 egrep 支持我们曾见过的 表示法。而另一些版本不
14、支持单独的起始和结束边界,只提供了统一的b元字符 (这个元字符我们还没见过,下一章才会用到) 。还有些工具同时支持这两种表示法,另有 许多工具哪种也不支持。我用“流派(flavor) ”这个词来描述所有这些细微的实现规定。这就好像不同的人说不同的方 言一样。从表面上看, “流派”指的是关于元字符的规定,但它的内容远远不止这些。即使两个程序都支持,它们可能对这两个元字符的意义有不同的理解,对单词的理 解也不相同。在使用具体的工具软件时,这个问题尤其重要。改进匹配时间的表达式,处理24小时制时间 v 26页问题的答案办法有许多种,不过思路和之前差不多。现在我们把问题分为3部分:其一是上午(小 时数
15、从00到09,开头的0可选) ,其二是白天(小时数从10到19) ,其三是夜晚(小时数 从20到23) 。这样答案就很明显了:0?0-9|10-9|20-3。实际上,我们可以合并头两个多选分支,得到01?0-9|20-3 。你可能需要动点脑筋 才能明白这个表达式与上面是完全等价的。下面的图可能有所帮助,它还提供了另一种 思路。阴影部分表示单个多选分支能够匹配的数字。左图 右图请不要混淆“流派(flavor) ”和“工具(tool) ”这两个概念。两个人可以说同样的方言,两个完 全不同的程序也可能属于同样的流派。同样,两个名字相同的程序(解决的任务也相同)所 属的流派可能有细微(有时可能并非细微)的差别。有许多程序都叫 egrep,它们所属的流 派也五花八门。由 Perl 语言的正则表达式开创的流派,在20世纪90年代中期因为其强大的表达能力广为人们 所知,其他语言紧随其后,提供了汲取其中灵感的正则表达式(其中许多为了标明自己的思 想来源,直接给自己贴上“兼容 Perl(Perl-Compatible) ”的标签) 。它们包括 PHP、Python、Java 的大量正则包, 微软的.NET Framework、Tcl,以及 C 的各种类库。不 过,所有这些语言在重要的方面各有不同。而且 Perl