正则表达式 (Regular Expression) 对字符串的匹配很强大也很方便,因此做一些记录。
特殊字符
特殊字符 | 描述 | 备注 |
---|---|---|
. | 匹配一个除换行符之外的任意字符。 | |
\d | 匹配一个数字。等价于 [0-9]。 | |
\D | 匹配一个非数字。等价于 [^0-9]。 | |
\w | 匹配一个字母或数字或下划线。 | \w 匹配的字母不止英文字母,因此比 [A-Za-z0-9_] 范围更广。 |
\W | 匹配一个非字母或数字或下划线。 | |
\s | 匹配一个任意空白字符,包括空格、制表符、换页符等。等价于 [ \f\n\r\t\v]。 | |
\S | 匹配一个任意非空白字符。等价于 [^ \f\n\r\t\v]。 | |
^ | 匹配行的开头。 | 如 ^\d 表示匹配以数字开头的行。 |
$ | 匹配行的结尾。 | 如 \d$ 表示匹配以数字结尾的行。 |
* | 匹配前面的子表达式零次或多次。 | 如 abc* 能匹配 ab 、 abc 或 abcc 等。 |
+ | 匹配前面的子表达式一次或多次。 | 如 abc+ 能匹配 abc 或 abcc 等,但不能匹配 ab。 |
? | 匹配前面的子表达式零次或一次。 | 如 abc? 能匹配 ab 或 abc。 |
{n} | 匹配前面的子表达式 n 次。 | 如 [A-Z]{5} 能匹配 5 个连续的大写字母。 |
{n,} | 匹配前面的子表达式至少 n 次。 | 如 [a-z]{3,} 能匹配至少 3 个连续的小写字母。 |
{n,m} | 匹配前面的子表达式 n-m 次。 | 如 \d{4,6} 能匹配 4-6 个连续的数字。 |
(XYZ) | 将() 中的内容视为一个整体。 | 如 p(abc)+ 将匹配 pabc 或 pabcabc 等。 |
[XYZ] | 将[] 中为一个集合,匹配该集合中的任意一个字符。 | 可用 - 表示范围,如 [A-Za-z0-9] 能匹配任意大小写英文字母和数字一次。 |
[^XYZ] | 负值集合,匹配非该集合中的任意一个字符。 | |
X|Y | 匹配 X 或 Y。 | |
\b | 匹配一个单词边界,即单词和空格间的位置。 | 如 \ber 可以匹配 erase,但不能匹配 verb 或 server。 |
\B | 匹配一个非单词边界。 | |
< \> | 匹配单词的开始 (<) 和结尾 (\>) | 如 ion\> 可以匹配 onion,但不能匹配 sionox 或 ionic。 |
因此需要使用 \ 转义的字符有 |
\
[
]
{
}
(
)
?
+
.
*
^
$
贪婪匹配
正则默认是进行贪婪匹配,意思是会匹配最长的符合条件的子串。
如在匹配注释时使用:/\*.*\*/
可匹配 C 语言中 /*xxxxxxxxx*/
的块注释。
但如果有以下内容:
/*aaaaaaaaaaa*/ xxxxxxx /*bbbbbbbbbbbbbbb*/ yyyyyyy /*ccccccccc*/
使用 /\*.*\*/
会全部匹配,但如果在 *
或 +
等通配符后加一个 ?
,则会进行最短匹配。
即 /\*.*?\*/
可匹配 /*aaaaaaaaaaa*/
、/*bbbbbbbbbbbbbbb*/
和 /*ccccccccc*/
。
环视
有时需要检查 pattern 前或后的字符串是否符合,但又不想匹配他们,因此就需要用到环视。环视包括前瞻和后顾,都包括肯定或否定。
特殊字符 | 描述 | 备注 |
---|---|---|
(?=pattern) | 后顾肯定检查 | 如 abc(?=x)会匹配到 abcx 中的 abc,而非 abcd 中的 abc |
(?!pattern) | 后顾否定检查 | 如 abc(?!x)会匹配到 abcd 中的 abc,而非 abcx 中的 abc |
(?<=pattern) | 前瞻肯定检查 | 如 (?<=x)abc会匹配到 xabc 中的 abc,而非 aabc 中的 abc |
(?<!pattern) | 前瞻否定检查 | 如 (?<!x)abc会匹配到 aabc 中的 abc,而非 xabc 中的 abc |
值得注意的是,环视是非获取匹配,例如 CD(?=EF)G 是匹配不到 CDEFG 的,要用 CD(?=EF)E 才可以。
分组语法捕获
这里就不得不说后向引用的概念。
使用小括号指定一个子表达式后,匹配这个子表达式的文本可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
后向引用用于重复搜索前面某个分组匹配的文本。
比如 要匹配这个字符串 Hello world? Hello world!
,就可以用 (Hello) (world)\? \1 \2\!
,其中 \1
和 \2
就是正则中前面用小括号括起来的组号。
这时候就有 (?:pattern)
,匹配 pattern,不捕获匹配的文本。这时 (?:Hello) (world)\? \1
中的 \1
就是 world 了。
还有 (?<name>pattern)
,匹配 pattern,并捕获文本到名称为 name 的组里,也可以写成(?'name’exp)。
注释
(?#comment)
这种不对正则表达式的处理产生任何影响,只是为了提供让人阅读注释。