匹配某范围的字符
有时需要查找某些特定的字符时怎么辨?这时中括号”[]”就派上了用场。因此[aeiou]所要查找的是”a”、”e”、”i”、”o”、”u”这些元音,[.?!]所要查找的是”.”、”?”、”!”这些符号,在中括号中的特殊字符的特别意义都会被移除,也就是解译成单纯的字面意义。也可以指定某些范围的字符,如”[a-z0-9]”,所指的就是任意小写字母或任意数字。
接下来再看一个比较初复杂查找电话号码的RE例子
14. \(?\d{3}[( ] \s?\d{3}[- ]\d{4} (查找十位数字之电话号码,如(080) 333-1234 )
这样的RE可查找出较多种格式的电话号码,如(080) 123-4567、511 254 6654等。”\(?”代表一个或零个左小括号”(“,而”[( ]”代表查找一个右小括号”)”或空格符,”\s?”指一个或零个空格符组。但这样的RE会将类似”800) 45-3321”这样的电话找出来,也就是括号没有对称平衡的问题,之后会学到择一(alternatives)来决解这样的问题。
不包含在某特定字符组里(Negation)
有时需要查找在包含在某特定字符组里的字符,下表说明如何做类似这样的描述。
\W 不是字母数字的任意字符
\S 不是空格符的任意字符
\D 不是数字字符的任意字符
\B 不在字边界的位置
[^x] 不是x的任意字符
[^aeiou] 不是a、e、i、o、u的任意字符
15. \S+ (不包含空格符的字符串)
择一(Alternatives)
有时会需要查找几个特定的选择,此时”|”这个特殊字符就派上用场了,举例来说,要查找五个数字及九个数字(有”-”号)的邮政编码。
16. \b\d{5}-\d{4}\b|\b\d{5}\b (查找五个数字及九个数字(有”-”号)的邮政编码)
在使用Alternatives时需要注意的是前后的次序,因为RE在Alternatives中会优先选择符合最左边的项目,16中,如果把查找五个数字的项目放在前面,则这RE只会找到五个数字的邮政编码。了解了择一,可将14做更好的修正。
17. (\(\d{3}\)|\d{3})\s?\d{3}[- ]\d{4} (十个数字的电话号码)
群组(Grouping)
括号可以用来介定一个次描述,经由次描述的介定,可以针对次描述做重复或及他的处理。
18. (\d{1,3}\.){3}\d{1,3} (寻找网络地址的简单RE)
此RE的意思第一个部分(\d{1,3}\.){3},所指的是,数字最小一位最多三位,并且后面接有”.”符号,此类型的共有三个,之后再接一到三位的数字,也就是如192.72.28.1这样的数字。
但这样会有个缺点,因为网络地址数字最多只到255,但上述的RE只要是一到三位的数字都是符合的,所以这需要让比较的数字小于256才行,但只单独使用RE并无法做这样的比较。在19中使用择一来将地址的限制在所需要的范围内,也就是0到255。
19. ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?) (寻找网络地址)
有没有发觉RE越来越像外星人说的话了?就以简单的寻找网络地址,直接看RE都满难理解的哩。
Expresso Analyzer View
Expresso提供了一个功能,它可以将所下的RE变成树状的说明,一组组的分开说明,提供了一个好的除错环境。其它的功能,如部分符合(Partial Match只查找反白RE的部分)及除外符合(Exclude Match只不查找反白RE的部分)就留给各位大大试试啰。
当次描述用括号群组起来时,符合次描述的文字可用在之后的程序处理或RE本身。在预设的情型下,所符合的群组是由数字命名,由1开始,由顺序是由左至右,这自动群组命名,可在Expresso中的skeleton view或result view中看到。
Backreference是用来查找群组中抓取的符合文字所相同的文字。举例来说”\1”所指符合群组1所抓取的文字。
20. \b(\w+)\b\s*\1\b (寻找重复字,此处说的重复是指同样的字,中间有空白隔开如dog dog这样的字)
(\w+)会抓取至少一个字符的字母或数字的字,并将它命名为群组1,之后是查找任意空格符,再接和群组1相同的文字。
如果不喜欢群组自动命名的1,也可以自行命名,以上述例子为例,(\w+)改写为(?<word>\w+),这就是将所抓取的群组命名为word,Backreference就要改写成为\k<word>
21. \b(?<word>\w+)\b\s*\k<word>\b (使用自行命名群组抓取重复字)
使用括号还有许多特别的语法元素,比较通用的列表如下:
抓取(Captures)
(exp) 符合exp并抓取它进自动命名的群组
(?<name>exp) 符合exp并抓取它进命名的群组name
(?:exp) 符合exp,不抓取它
Lookarounds
(?=exp) 符合字尾为exp的文字
(?<=exp) 符合前缀为exp的文字
(?!exp) 符合后面没接exp字尾的文字
(?<!exp) 符合前面没接exp前缀的文字
批注Comment
(?#comment) 批注
Positive Lookaround
接下来要谈的是lookahead及lookbehind assertions。它们所查找的是目前符合之前或之后的文字,并不包含目前符合本身。这些就如同”^”及”\b”特殊字符,本身并不会对应任何文字(用来界定位置),也因此称做是zero-width assertions,看些例子也许会清楚些。
(?=exp)是一个”zero-width positive lookahead assertion”。它指的就是符合字尾为exp的文字,但不包含exp本身。
22. \b\w+(?=ing\b) (字尾为ing的字,比如说filling所符合的就是fill)
(?<=exp)是一个”zero-width positive lookbehind assertion”。它指的就是符合前缀为exp的文字,但不包含exp本身。
23. (?<=\bre)\w+\b (前缀为re的字,比如说repeated所符合的就是peated)
24. (?<=\d)\d{3}\b (在字尾的三位数字,且之前接一位数字)
25. (?<=\s)\w+(?=\s) (由空格符分隔开的字母数字字符串)
Negative Lookaround
之前有提到,如何查找一个非特定或非在特定群组的字符。但如果只是要验证某字符不存在而不要对应这些字符进来呢?举个例子来说,假设要查找一个字,它的字母里有q但接下来的字母不是u,可以用下列的RE来做。
26. \b\w*q[^u]\w*\b (一个字,其字母里有q但接下来的字母不是u)
这样的RE会有一个问题,因为[^u]要对应一个字符,所以若q是字的最后一个字母,[^u]这样的下法就会将空格符对应下去,结果就有可能会符合二个字,比如说”Iraq haha”这样的文字。使用Negative Lookaround就能解决这样的问题。
27. \b\w*q(?!u)\w*\b (一个字,其字母里有q但接下来的字母不是u)
这是”zero-width negative lookahead assertion”。
28. \d{3}(?!\d) (三个位的数字,其后不接一个位数字)
同样的,可以使用(?<!exp),”zero-width negative lookbehind assertion”,来符合前面没接exp前缀的文字符串。
29. (?<![a-z ])\w{7} (七个字母数字的字符串,其前面没接字母或空格)
30. (?<=<(\w+)>).*(?=<\/\1>) (HTML卷标间的文字)
这使用look