元字符
代码 | 说明 |
---|---|
. | 匹配 换行符 以外的任意字符 |
\w | 匹配 字母 数字 下划线 汉字 |
\s | 匹配 任意空白符 |
\d | 匹配 数字 |
^ | 匹配 字符串的开始 |
$ | 匹配 字符串的结束 |
转义字符
匹配元字符本身时使用
重复匹配
代码 | 说明 |
---|---|
+ | 匹配 1次或多次 |
* | 匹配 0次或多次 |
? | 匹配 0次或1次 |
{n} | 匹配 n次 |
{min / max} | 匹配 最少min次, 最多max次 |
{min, } | 匹配 最少min次 |
字符类
代码 | 说明 |
---|---|
[] | 匹配 任意方括号中的字符 |
[aeiou] | 匹配 元音字母 |
[0-9] | 相当于 \d |
[a-z0-9A-Z_] | 相当于\w |
反义
代码 | 说明 |
---|---|
\W | 匹配 不是\w的字符 |
\S | 匹配 不是空白符的字符 |
\D | 匹配 非数字的字符 |
\B | 匹配 不是单词开头或者结束的位置 |
[^x] | 匹配 除了x以外的任意字符 |
[^aeiou] | 匹配 除了aeiou这几个字母以外的任意字符 |
分支条件
代码 | 说明 |
---|---|
| | 将不同的匹配条件分隔 存在短路现象 |
分组
代码 | 说明 |
---|---|
() | 创建一个括号中的子表达式 |
后向引用
组号分配
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推
实际上
- 分组0对应整个正则表达式
- 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
- 你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。
\b(\w+)\b\s+\1\b 可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。
你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成’也行:(?’Word’\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s+\k<Word>\b。
分类 | 代码 | 说明 |
---|---|---|
捕获 | (exp) | 匹配exp, 并捕获文本到自动命名的组里 |
捕获 | (? |
匹配exp, 并捕获文本到名称为name的组里,也可以写成(?’name’exp) |
捕获 | (?:exp) | 匹配exp, 不捕获匹配的文本,也不给此组分配组号 |
零宽断言 | (?=exp) | 匹配exp前面的位置 |
零宽断言 | (?<=exp) | 匹配exp后面的位置 |
零宽断言 | (?!exp) | 匹配后面跟的不是exp的位置 |
零宽断言 | (!<!exp) | 匹配前面不是exp的位置 |
注释 | (?#comment) | 注释 |
零宽断言
代码 | 说明 |
---|---|
\b | 匹配位置 它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w。 |
^ | 字符串的开始 |
$ | 字符串的结束 |
(?=exp) | 零宽度正预测先行断言, 断言自身出现的位置的后面能匹配表达式exp |
(?<=exp) | 零宽单正回顾后发断言, 断言自身出现的位置的前面能匹配表达式exp |
负向零宽断言
代码 | 说明 |
---|---|
(?!exp) | 零宽度负预测先行断言, 匹配 后面跟的不是exp的位置 |
(!<!exp) | 零宽度负回顾后发断言, 匹配 前面不是exp的位置 |
注释
小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249) | 250-5 | [01]?\d\d?(?#0-199)。 |
要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:
(?<= # 断言要匹配的文本的前缀
<(\w+)> # 查找尖括号括起来的内容
# (即HTML/XML标签)
) # 前缀结束
.* # 匹配任意文本
(?= # 断言要匹配的文本的后缀
<\/\1> # 查找尖括号括起来的内容
# 查找尖括号括起来的内容
) # 后缀结束
贪婪与懒惰
-
默认贪婪
-
在数量限定符之后加上? 开启懒惰
-
比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。
代码 | 说明 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或多次, 但尽可能少重复 |
?? | 重复0次或多次, 但尽可能少重复 |
{n, m}? | 重复n到m次, 但尽可能少重复 |
{n, }? | 重复n次以上, 但尽可能少重复 |
处理选项
名称 | 说明 |
---|---|
IgnoreCase(忽略大小写) | 匹配时不区分大小写。 |
Multiline(多行模式) | 更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.) |
Singleline(单行模式) | 更改.的含义,使它与每一个字符匹配(包括换行符\n)。 |
IgnorePatternWhitespace(忽略空白) | 忽略表达式中的非转义空白并启用由#标记的注释。 |
ExplicitCapture(显式捕获) | 仅捕获已被显式命名的组。 |
单行模式和多行模式并不冲突
平衡组/递归匹配
有时我们需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用(.+)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?
代码 | 说明 |
---|---|
(?’group’exp) | 将捕获的内容命名为group, 并压入堆栈 |
(?‘-group’exp) | 从堆栈中弹出exp, 否则匹配失败 |
(?(group)yes|no) | 如果堆栈中存在group的话, 继续匹配yes部分的表达是, 否则继续匹配no部分 |
(?!) | 零宽负向先行断言,由于没有后缀表达式, 试图匹配总是失败 |
把xx <aa <bbb> <bbb> aa> yy这样的字符串里,最长的配对的尖括号内的内容捕获出来
< #最外层的左括号
[^<>]* #它后面非括号的内容
(
(
(?'Open'<) #左括号,压入"Open"
[^<>]* #左括号后面的内容
)+
(
(?'-Open'>) #右括号,弹出一个"Open"
[^<>]* #右括号后面的内容
)+
)*
(?(Open)(?!)) #最外层的右括号前检查
#若还有未弹出的"Open"
#则匹配失败
> #最外层的右括号
平衡组的一个最常见的应用就是匹配HTML,下面这个例子可以匹配嵌套的<div>标签:
<div[^>]*>[^<>]*(((?'Open'<div[^>]*>)[^<>]*)+((?'-Open'</div>)[^<>]*)+)*(?(Open)(?!))</div>
其他
代码 | 说明 |
---|---|
\a | 报警字符(打印它的效果是电脑嘀一声) |
\b | 通常是单词分界位置,但如果在字符类里使用代表退格 |
\t | 制表符,Tab |
\r | 回车 |
\v | 竖向制表符 |
\f | 换页符 |
\n | 换行符 |
\e | Escape |
\0nn | ASCII代码中八进制代码为nn的字符 |
\xnn | ASCII代码中十六进制代码为nn的字符 |
\unnnn | Unicode代码中十六进制代码为nnnn的字符 |
\cN | ASCII控制字符。比如\cC代表Ctrl+C |
\A | 字符串开头(类似^,但不受处理多行选项的影响) |
\Z | 字符串结尾或行尾(不受处理多行选项的影响) |
\z | 字符串结尾(类似$,但不受处理多行选项的影响) |
\G | 当前搜索的开头 |
\p{name} | Unicode中命名为name的字符类,例如\p{IsGreek} |
(?>exp) | 贪婪子表达式 |
(? |
平衡组 |
(?im-nsx:exp) | 在子表达式exp中改变处理选项 |
(?im-nsx) | 为表达式后面的部分改变处理选项 |
(?(exp)yes|no) | 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no |
(?(exp)yes) | 同上,只是使用空表达式作为no |
(?(name)yes|no) | 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no |
(?(name)yes) | 同上,只是使用空表达式作为no |