正则表达式定义
- 正则表达式(regular expression)就是用一个“字符串”来描述一个特征,然后去验证另一个“字符串”是否符合这个特征。
- 正则表达式可以用来:
- 验证字符串是否符合指定特征,比如验证是否是合法的邮件地址。
- 用来查找字符串,从一个长的文本中查找符合指定特征的字符串,比查找固定字符串更加灵活方便。
- 用来替换,比普通的替换更强大。
符号介绍
普通字符和简单的转义字符
- 普通字符
- 字母、数字、汉字、下划线、以及没有特殊定义的标点符号,都是"普通字符"。
- 匹配与之相同的一个字符。
- 简单的转义字符
- \r,\n代表回车和换行符
- \t 制表符
- \\ 代表"\" 本身
- 这些转义字符的匹配方法与 "普通字符" 是类似的。也是匹配与之相同的一个字符。
能够与 ‘多种字符’ 匹配的表达式
- \d 任意一个数字,0~9 中的任意一个
- \w 任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_ 中任意一个
- \s 包括空格、制表符、换页符等空白字符的其中任意一个
- . 小数点可以匹配除了换行符(\n)以外的任意一个字符
虽然可以匹配其中任意字符,但是只能是一个,不是多个。
自定义能够匹配 '多种字符' 的表达式
- 使用方括号 [ ] 包含一系列字符,能够匹配其中任意一个字符。用 [^ ] 包含一系列字符,则能够匹配其中字符之外的任意一个字符。
例如:[ab5@] 匹配 "a" 或 "b" 或 "5" 或 "@"
[^A-F0-3] 匹配 "A"~"F","0"~"3" 之外的任意一个字符
同样,虽然可以匹配其中任意一个,但是只能是一个,不是多个。
修饰匹配次数的特殊符号
可以让表达式重复匹配的符号。放在“被修饰的表达式”后边。
- {n} 表达式重复n次
- {m,n}表达式至少重复m次,最多重复n次
- {m,} 表达式至少重复m次
- ? 匹配表达式0次或者1次,相当于 {0,1}
- +表达式至少出现1次,相当于 {1,}
- * 表达式不出现或出现任意次,相当于 {0,}
举例:表达式 "\d+\.?\d*" 在匹配 "It costs $12.5" 时,匹配的结果是:成功;匹配到的内容是:"12.5";
一些符号在表达式中代表抽象的特殊意义
- ^ 与字符串开始的地方匹配,不匹配任何字符
- $ 与字符串结束的地方匹配,不匹配任何字符
- \b 匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符
举例1:表达式 “^aaa” 在匹配 “xxx aaa xxx” 时,匹配结果是:失败。因为 “^” 要求与字符串开始的地方匹配,因此,只有当 “aaa” 位于字符串的开头的时候,“^aaa” 才能匹配。
举例2:表达式 “.\b.” 在匹配 “@@@abc” 时,匹配结果是:成功;匹配到的内容是:“@a”。
进一步说明:"\b" 与 "^" 和 "$" 类似,本身不匹配任何字符,但是它要求它在匹配结果中所处位置的左右两边,其中一边是 "\w" 范围,另一边是 非"\w" 的范围。
举例3:表达式 “\bend\b” 在匹配 “weekend,endfor,end” 时,匹配结果是:成功;匹配到的内容是:“end”。
一些符号可以影响表达式内部的子表达式之间的关系
- | 左右两边表达式之间 "或" 关系,匹配左边或者右边
- ( )
- 在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰
- 取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到
举例1:表达式 “Tom|Jack” 在匹配字符串 “I‘m Tom, he is Jack” 时,匹配结果是:成功;匹配到的内容是:“Tom”。匹配下一个时,匹配结果是:成功;匹配到的内容是:“Jack”。
举例2:表达式 “(go\s*)+” 在匹配 “Let‘s go go go!” 时,匹配结果是:成功;匹配到内容是:“go go go”。
在表达式中有特殊意义,需要添加 “\” 才能匹配该字符本身的字符汇总
^ $ ( ) [ ] { } . ? + * |
高级规则
匹配次数中的贪婪与非贪婪
- 贪婪模式
- {m,}, “?”, “*”, “+"等在匹配的时候,总是尽可能多的匹配符合它规则的字符。
例如: (d)(\w+?)(d) 匹配 “dxxxdxxxd”时, "\w+" 将匹配第一个 "d" 和最后一个 "d" 之间的所有字符 "xxxdxxx"。
- 非贪婪模式
- 在修饰匹配次数的特殊符号后再加上一个 "?" 号,则可以使匹配次数不定的表达式尽可能少的匹配。这种匹配原则叫作 "非贪婪" 模式,也叫作 "勉强" 模式。
例如:(d)(\w+?)(d) 匹配“dxxxdxxxd”时,为了让整个表达式匹配成功,"\w+?" 不得不匹配 "xxx" 才可以让后边的 "d" 匹配,从而使整个表达式匹配成功。因此,结果是:"\w+?" 匹配 "xxx"
举例1:表达式 "<td>(.*)</td>" 与字符串 "<td><p>aa</p></td> <td><p>bb</p></td>" 匹配时,匹配的结果是:成功;匹配到的内容是 "<td><p>aa</p></td> <td><p>bb</p></td>" 整个字符串, 表达式中的 "</td>" 将与字符串中最后一个 "</td>" 匹配。
举例2:相比之下,表达式 "<td>(.*?)</td>" 匹配举例1中同样的字符串时,将只得到 "<td><p>aa</p></td>", 再次匹配下一个时,可以得到第二个 "<td><p>bb</p></td>"。
反向引用 \1, \2...
- 表达式在匹配时,表达式引擎会将小括号 ( ) 包含的表达式所匹配到的字符串记录下来。其实,"小括号包含的表达式所匹配到的字符串" 不仅是在匹配结束后才可以使用,在匹配过程中也可以使用。
- 表达式后边的部分,可以引用前面 "括号内的子匹配已经匹配到的字符串"。引用方法是 "\" 加上一个数字。"\1" 引用第1对括号内匹配到的字符串,"\2" 引用第2对括号内匹配到的字符串……以此类推,如果一对括号内包含另一对括号,则外层的括号先排序号。换句话说,哪一对的左括号 "(" 在前,那这一对就先排序号。
举例1:表达式 "('|")(.*?)(\1)" 在匹配 " 'Hello', "World" " 时,匹配结果是:成功;匹配到的内容是:" 'Hello' "。再次匹配下一个时,可以匹配到 " "World" "。
举例2:表达式 "(\w)\1{4,}" 在匹配 "aa bbbb abcdefg ccccc 111121111 999999999" 时,匹配结果是:成功;匹配到的内容是 "ccccc"。再次匹配下一个时,将得到 999999999。这个表达式要求 "\w" 范围的字符至少重复5次,注意与 "\w{5,}" 之间的区别。
举例3:表达式 "<(\w+)\s*(\w+(=('|").*?\4)?\s*)*>.*?</\1>" 在匹配 "<td id='td1' style="bgcolor:white"></td>" 时,匹配结果是成功。如果 "<td>" 与 "</td>" 不配对,则会匹配失败;如果改成其他配对,也可以匹配成功。
正向预搜索
前面几个代表抽象意义的特殊符号:“^”,“$”,“\b”。它们都有一个共同点,那就是:它们本身不匹配任何字符,只是对 “字符串的两头” 或者 “字符之间的缝隙” 附加了一个条件。预搜索则是对 “两头” 或者 “缝隙”
- 正向预搜索:"(?=xxxxx)","(?!xxxxx)“
- 格式:"(?=xxxxx)",在被匹配的字符串中,所在缝隙的右侧,必须能够匹配上 xxxxx 这部分的表达式。
举例1:表达式 "Windows (?=NT|XP)" 在匹配 "Windows 98, Windows NT, Windows 2000" 时,将只匹配 "Windows NT" 中的 "Windows ",其他的 "Windows " 字样则不被匹配。
举例2:表达式 "(\w)((?=\1\1\1)(\1))+" 在匹配字符串 "aaa ffffff 999999999" 时,将可以匹配6个"f"的前4个,可以匹配9个"9"的前7个。这个表达式可以读解成:重复4次以上的字母数字,则匹配其剩下最后2位之前的部分。
- 格式:"(?!xxxxx)",所在缝隙的右侧,必须不能匹配 xxxxx 这部分表达式。
举例3:表达式 "((?!\bstop\b).)+" 在匹配 "fdjka ljfdl stop fjdsla fdj" 时,将从头一直匹配到 “ stop " 之前的位置,如果字符串中没有 “ stop ",则匹配整个字符串。
举例4:表达式 "do(?!\w)" 在匹配字符串 "done, do, dog" 时,只能匹配 "do"。在本条举例中,"do" 后边使用 "(?!\w)" 和使用 "\b" 效果是一样的。
反向预搜索
- 反向预搜索:"(?<=xxxxx)","(?<!xxxxx)"
- 两种格式的概念和正向预搜索是类似的,反向预搜索要求的条件是:所在缝隙的 “左侧”,两种格式分别要求必须能够匹配和必须不能够匹配指定表达式。
举例5:表达式 "(?<=\d{4})\d+(?=\d{4})" 在匹配 "1234567890123456" 时,将匹配除了前4个数字和后4个数字之外的中间8个数字。
其他通用规则
- 在表达式 "\s","\d","\w","\b" 表示特殊意义的同时,对应的大写字母表示相反的意义
- \S 匹配所有非空白字符("\s" 可匹配各个空白字符)
- \D 匹配所有的非数字字符
- \W 匹配所有的字母、数字、下划线以外的字符
- \B 匹配非单词边界,即左右两边都是 "\w" 范围或者左右两边都不是 "\w" 范围时的字符缝隙
- 括号 "( )" 内的子表达式,如果希望匹配结果不进行记录供以后使用,可以使用 "(?:xxxxx)" 格式
举例1:表达式 "(?:(\w)\1)+" 匹配 "a bbccdd efg" 时,结果是 "bbccdd"。括号 "(?:)" 范围的匹配结果不进行记录,因此 "(\w)" 使用 "\1" 来引用。
注意事项
- 表达式不要匹配空字符串。否则会一直得到匹配成功,而结果什么都没有匹配到。
比如:准备写一个匹配 "123"、"123."、"123.5"、".5" 这几种形式的表达式时,整数、小数点、小数数字都可以省略,但是不要将表达式写成:"\d*\.?\d*",因为如果什么都没有,这个表达式也可以匹配成功。更好的写法是:"\d+\.?\d*|\.\d+"。
- 能匹配空字符串的子匹配不要循环无限次。
如果括号内的子表达式中的每一部分都可以匹配 0 次,而这个括号整体又可以匹配无限次,那么情况可能比上一条所说的更严重,匹配过程中可能死循环。
- 或 "|" 的左右两边,对某个字符最好只有一边可以匹配,这样,不会因为 "|" 两边的表达式因为交换位置而有所不同。