正则表达式

元字符

代码 | 说明

  •   | -
    

. | 匹配除换行符以外的所有字符
\w | 匹配字母或数字或下划线或汉字
\s | 匹配任意的空白符
\d | 匹配数字
^ | 匹配字符串的开始
$ | 匹配字符串的结束


### 字符转义 如果想查找元字符本身,需要用到\取消这些字符的特殊意义。比如,你要查找.或者$,应该使用\.或者\$。当然,要查找\本身,你也得用\\。
### 重复 代码/语法 | 说明 - | - * | 零次或更多次 + | 一次或更多次 ? | 零次或一次 {n} | 重复n次 {n,} | 重复n次或更多次 {n,m} | 重复n次到m次
### 字符类 [aeiou] 匹配任何一个英文元音字母

[.?!]
匹配标点符号(.或?或!)

[0-9]
代表的含义与\d就是完全一致的

[a-z0-9A-Z]
完全等同于\w


### 分枝条件 正则表达式里的分枝条件指的是几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不用的规则分隔开。注意,使用分枝条件时,要注意各个条件的顺序。

例子:0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。


### 分组 重复单个字符(直接在字符后面加上限定符就行了);重复多个字符,可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了。

例如:
({\d{1,3}.}){3}\d{1,3}是个一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字,(\d{1,3}.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。

不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?).){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)。

理解这个表达式的关键是理解2[0-4]\d|25[0-5]|[01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。


### 反义

代码/语法 | 说明

  •           |-
    

\W | 匹配任意不是字母,数字,下划线,汉字的字符
\S | 匹配任意不是空白符的字符
\D | 匹配任意非数字的字符
\B | 匹配不是单词开头或结束的位置
[^x] | 匹配除了x以外的任意字符
[^aeiou] | 匹配除了aeiou这几个字母以外的任意字母

反向引用

使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:

表4.常用分组语法

|分类 | 代码/语法 | 说明

  •               | -                                  |-         
    

|捕获 | (exp) | 匹配exp,并捕获到文本到自动命名的组里
|同上 | (?exp) | 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
|同上 | (?:exp) | 匹配exp,不捕获匹配的文本,也不给次分组分配租号
|零宽断言 | (?=exp) | 匹配exp前面的位置
|同上 | (?<=exp) | 匹配exp后面的位置
|同上 | (?!exp) | 匹配后面跟的不是exp的位置
|同上 | (?<!exp) | 匹配前面不是exp的位置
|注释 | (?#comment) | 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

零宽断言

下面四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。

断言用来声明一个应该为真的事实。正则表达只有当断言为真时才会继续进行匹配。

负向零宽断言(有点复杂)

前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样:

\b\wq[u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[u]\w\b就能匹配整个Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\wq(?!u)\w\b。

零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。

同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。

请详细分析表达式(?<=<(\w+)>).*(?=</\1>),这个表达式最能表现零宽断言的真正用途。

一个更复杂的例子:(?<=<(\w+)>).(?=</\1>)匹配不包含属性的简单HTML标签内里的内容。(?<=<(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是<b>),然后是.(任意的字符串),最后是一个后缀(?=</\1>)。注意后缀里的/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括前缀和后缀本身)。

注释

小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|250-5|[01]?\d\d?(?#0-199)。

要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:

(?<=         # 断言要匹配的文本的前提
<(\w+)>      # 查找尖括号括起来的字母或数字(即HTML/XML标签)
)            # 前缀结束
.*           # 匹配任意文本
(?=          # 断言要匹配的文本的后缀
<\/\1>       # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签
)            # 后缀结束

贪婪与懒惰

表5.懒惰限定符

代码/语法 | 说明

  •                  | -
    

*? | 重复任意次,但尽可能少重复
+? | 重复1次或更多次,但尽可能少重复
?? | 重复0次或1次,但尽可能少重复
{n,m}? | 重复n都m次,但尽可能少重复
{n,}? | 重复n次以上,但尽可能少重复

处理选项

平衡组/递归匹配

摘选的文章里的平衡组语法是由.Net Framework支持的;其他语言/库不一定支持这种功能,或者支持此功能但需要使用不同的语法。

其他

下面是一些未提到的元素的列表,包含语法和简单的说明。

表7.尚未详细讨论的语法

代码/语法 | 说明

  •                    |    -
    

\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) | 贪婪子表达式
(?<x>-<y>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

参考:
让你快速掌握正则表达式

posted on 2018-12-04 23:47  粤B_Liao  阅读(188)  评论(0编辑  收藏  举报

导航