字符
表示单个字符。例如a,它将匹配字符串中第一次出现的字符a;它也可以匹配第二个 a,这必须是你告诉正则表达式引擎从第一次匹配的地方开始搜索。
可以使用特殊字符序列代表某些不可显示字符。
构造 | 匹配 |
B | 字符B |
\xhh | 十六进制值为oxhh的字符 |
\uhhhh | 十六进制值为oxhhhh的Unicode字符 |
\t | 制表符Tab:(‘\u0009’) |
\r | 回车符:(‘\u000D’) |
\n | 换行符:(‘\u000A’) |
\f | 换页符:(‘\u000C’) |
\e | 转义符(Escape):(‘\u001B’) |
字符集
字符集是由一对括号[]括起来的字符集合。使用字符集,可以告诉正则表达式引擎仅仅匹配多个字符中的一个。
字符集可以出现在其它字符集中,并且可以包含并集运算符(隐式)和交集运算符 (&&)。
构造 | 匹配 |
[abc] | a、b或c(等效于a|b|c) |
[^abc] | 除了a、b或c的任何字符(否定) |
[a-zA-Z] | a到z或A到Z的任何字符(范围) |
[abc[hij]] | a、b、c、h、i或j,等效于a|b|c|h|i|j(并集) |
[a-z&&[hij]] | h、i或j(交集) |
并集和交集的应用示例如下:
[a-c[h-j]]
a到c或h到j的任何字符(并)
[a-z&&[^hij]]
a到z,除了h、i、j(减去),等效于[a-gk-z]
[a-z&&[^h-j]]
a到z,而非h到j(减去),等效于[a-gk-z]
预定义字符集
预定义字符集可以用在方括号之内或之外。
例如\s\d匹配一个空白符后面紧跟一个数字;[\s\d]匹配单个空白符或数字。
构造 | 匹配 |
\d | 数字:[0-9] |
\D | 非数字:[^0-9] |
\s | 空白字符:空格、制表、回车、换行、换页 |
\S | 非空白字符:[^\s] |
\w | 单词字符:[a-zA-Z_0-9] |
\W | 非单词字符:[^\w] |
边界匹配符
构造 | 匹配 |
^ | 输入序列的开始 当启用多行模式后,还可以匹配行的开始 |
$ | 输入序列的结尾 当启用多行模式后,还可以匹配行的结尾 |
\b | 单词边界
|
\B | 非单词边界:[^\b] \B匹配的位置是两个“单词字符”之间或两个“非单词字符”之间的位置 |
\G | 上一个匹配的结尾 |
逻辑操作符
构造 | 匹配 |
XY | X后跟Y |
X|Y | X或Y 匹配分支条件时,将会从左到右地测试每个条件,如果满足了某个分支的话,就不会去再管其它的条件了。例如\d{5}|\d{5}-\d{4},只会匹配5位数字以及9位数字的前5位 |
(X) | 捕获组(capturing group)。可以在表达式中用\i引用第i个捕获组 |
量词
量词描述了一个模式吸收输入文本的模式。
贪婪型:量词总是贪婪的,除非有其它的选项被设置。贪婪表达式会为所有可能的模式发现尽可能多的匹配。
懒惰型:懒惰型表达式匹配满足模式所需的最少字符数。
占有型:目前,占有型量词只在Java语言中可用。当正则表达式被应用于字符串时,它会产生相当多的状态,以便在匹配失败时可以回溯。而占有型量词并不保存这些中间状态,因此它可以防止回溯。它们常常用来防止正则表达式失控,因此可以使正则表达式执行起来更有效。
贪婪型 | 懒惰型 | 占有型 | 如何匹配 |
X? | X?? | X?+ | 0次或1次X(等效于X{0,1}) 事实上表示前导字符是可选的 |
X* | X*? | X*+ | 0次或多次X(等效于X{0,} ) 事实上表示前导字符可以出现任意次数 |
X+ | X+? | X++ | 1次或多次X(等效于X{1,} ) 事实上表示前导字符至少重现1次 |
X{n} | X{n}? | X{n}+ | 恰好n次X |
X{n,} | X{n,}? | X{n,}+ | 至少n次X |
X{n,m} | X{n,m}? | X{n,m}+ | X至少n次,且不超过m次 |
量词的贪婪性
量词?+*会导致正则表达式引擎尽可能的重复前导字符。只有当这种重复会引起整个正则表达式匹配失败的情况下,引擎会进行回溯。也就是说,它会放弃最后一次的“重复”,然后处理正则表达式余下的部分。
例如,用一个正则表达式匹配一个HTML标签。
输入字符串为”This is a <EM>first</EM> test”,正则表达式<.+>返回”<EM>first</EM>”。这是因为+是贪婪性的。
让我们来看看正则引擎的处理过程。
正则表达式的第一个符号是“<”,这是一个字符;第二个符号是“.”,匹配了字符“E”;第三个符号是“+”,它可以一直可以匹配后面的字符,直到一行的结束;然后遇到了换行符,匹配失败(“.”不匹配换行符)。于是引擎开始对下一个正则表达式符号进行匹配,也即试图匹配“>”。到目前为止,正则表达式“<.+”已经匹配了“<EM>first</EM> test”。引擎会试图将“>”与换行符进行匹配,结果失败了。于是引擎进行回溯,“<.+”匹配“<EM>first</EM> tes”,于是引擎将“>”与“t”进行匹配,显然还是会失败。这个过程继续,直到“<.+”匹配“<EM>first</EM”,“>”与“>”匹配。最后引擎找到了一个匹配的部分“<EM>first</EM>”。
因为正则导向的引擎是“急切的”,所以它会急着报告它找到的第一个匹配。而不是继续回溯,即使可能会有更好的匹配,例如“<EM>”。所以由于“+”的贪婪性,使得引擎返回了一个最长的匹配。
可以使用下列方式来解决量词的贪婪性带来的问题。
用懒惰性取代贪婪性。
输入字符串为”This is a <EM>first</EM> test”,正则表达式<.+?>返回”<EM>”。
让我们再来看看正则引擎的处理过程。
正则表达式符号“<”会匹配素如字符串的第一个“<”。下一个正则符号是“.”。这次是一个懒惰的“+?”来重复上一个字符,它会尽可能少的重复上一个字符。因此引擎匹配“.”和字符“E”,然后用“>”匹配“M”,结果失败了。引擎会进行回溯,和上一个例子不同,因为是惰性重复,所以引擎是扩展惰性重复而不是减少;于是“<.+”现在被扩展为“<EM”。引擎继续匹配下一个符号“>”。这次得到了一个成功匹配。引擎于是报告“<EM>”是一个成功的匹配。
惰性扩展的一个替代方案。
输入字符串为”This is a <EM>first</EM> test”,正则表达式<[^>]+>返回”<EM>”。
这是一个更好的替代方案。可以用一个贪婪重复与一个取反字符集<[^>]+>。之所以说这是一个更好的方案在于使用惰性重复时,引擎会在找到一个成功匹配前对每一个字符进行回溯。而使用取反字符集则不需要进行回溯。