正则表达式语法

风尘

文章目录

  1. 1. 元字符
  2. 2. 字符转义
  3. 3. 重复
  4. 4. 字符类
  5. 5. 分枝条件
  6. 6. 分组
  7. 7. 反义
  8. 8. 后向引用
  9. 9. 贪婪与懒惰
  10. 10. 修饰符(标记)

[TOC]

元字符

代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束

字符转义

如果想查找元字符本身的话,就需要将字符转义 如:\* \.

重复

代码 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

字符类

要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?

- [aeiou] 匹配元音字母
- [0-9] 匹配数字范围0-9(与\d含义一致)
- [a-z0-9A-Z_] 匹配字母数字下划线(只考虑英文时,与\w含义一致)

案例1:
匹配几种电话号码:
(010)88886666,或022-22334455,或02912345678

\(?0\d{2}[) -]?\d{8}

分枝条件

分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。

观察案例1表达式,发现其匹配存有以下缺陷:
能够同时匹配010)12345678或(022-87654321这种不正确的格式.因此,解决这种问题要使用分枝条件,如下:

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

- \(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}  
这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。  

- \d{5}-\d{4}|\d{5}  
这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。

注意: 用分枝条件时,要注意各个条件的顺序,上面例子如果改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了

分组

当想要重复多个字符时,可以使用小括号来指定子表达式(也叫做,分组),然后可以指定子表达式的重复次数.
案例2
匹配ip地址:

- (\d{1,3}\.){3}\d{1,3} 
简单的ip地址匹配,缺陷是会匹配到256.300.888.999这种不可能存在的IP地址

- ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
精确匹配ip地址,由于正则不支持算术比较,所以只能使用冗长的分组和分枝条件

反义

有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义

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

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本.
案例3
匹配重复的单词

- \b(\w+)\b\s+\1\b 匹配go go或kitty kitty这类单词(\1代表第一个分组)

组名也可以自定义,语法如下:
(?<name>exp)
案例3变形如下:

- \b(?<Word>\w+)\b\s+\k<Word>\b
注意:\k<name>表示使用定义的别名
后向引用其它用法总结:
分类 代码/语法 说明
捕获 (exp) 匹配exp,并捕获文本到自动命名的组里
(?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号(一般用于只验证规则,不需要捕获内容)
零宽断言 (?=exp) 匹配exp前面的位置
(?<=exp)< td> 匹配exp后面的位置
负向零宽断言 (?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
注释 (?#comment) 此类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读
`案例4`匹配以 ing 结尾的单词前面的部分(除 ing 以外部分)
going
\w+(?=ing) 零宽断言
go

贪婪与懒惰

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符.如:
a.b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配*。
相反,有时需要尽可能少的匹配,称为懒惰匹配,语法是在表达式之后加一个?.同上面表达式变成懒惰匹配 : a.*?b

代码/语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

修饰符(标记)

正则表达式的标记用于指定额外的匹配策略。标记不写在正则表达式里,标记位于表达式之外。

格式:/pattern/flags

修饰符 说明
i (ignore)忽略大小写。
g 全局匹配。默认正则过程中字符串从左到右匹配,找到第一个符合条件的即匹配成功。使用该修饰符,查找所有匹配项。
m 多行模式,边界字符 `^` 和 `$` 匹配每一行的开头和结尾,而不是整个字符串的开头和结尾。
s `“.”`表达式包含换行符`“\n”`。默认情况下的`"."` 是 匹配除换行符 `"\n"` 之外的任何字符,加上 `s` 修饰符之后包含换行符 `"\n"`。
u 完整的 unicode 支持。默认正则不支持四个字节的 UTF-16 编码,会将其识别为两个字符。该修饰符将会正确识别为一个字符,建议汉字匹配都加上该修饰符。
y (sticky)“粘连”修饰符,与`g`类似也是全局匹配,不同之处在于,`g`修饰符只要剩余位置中存在匹配就可,而`y`修饰符确保匹配必须从剩余的第一个位置开始。有些实现需要与`g`一起使用才可全局匹配。
# u 修饰符
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true

/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true

# y 修饰符
字符串:a_a_a_a_aa_a_a_
正则:/a_/gy
匹配结果:a_a_a_a_