前言
说起正则表达式( Regular Expression ),很多人都会头疼,记命令都要记得吐血,不过正则表达式的效率真的是高的一比,完全可以从文本中筛选出你想要的任何内容,所以还是得学啊,并且如果没有正则表达式的话, Linux 也不会那么高效。这玩意以前已经学习过一遍了,没有怎么练习加上过去了好久又给全忘了,因此又得重新再学一遍 == ,其实也没有太多东西,但是要经常练习才能熟练。
常用元字符
元字符不是普通的字符,是代表了某种意义的字符,这里就把基本的元字符给记下来 (如果要匹配的东西是元字符的话要用 转义)
元字符 | 意义 |
---|---|
b | 匹配单词的开始或结束( 中间不少于 1 个 w ) |
w | 匹配字母、数字、下划线、汉字 |
s | 匹配任意的空白符( Tab、空格、换行符 ) |
d | 匹配一个数字 |
. | 匹配除换行符之外的任意字符 |
^ | 匹配字符串的开头 |
$ | 匹配字符串的结尾 |
[] | 匹配 [] 里的内容 |
() | 给括号内表达式分组 |
B | 匹配不是单词开头或结束的位置 |
W | 匹配任意不是字⺟,数字,下划线,汉字的字符,相当于 [^w] |
S | 匹配任意不是空⽩符的字符 |
D | 匹配任意⾮数字的字符,相当于 [^d] |
#这里说的单词并不是英语单词,是不少于一个 w
的东西,更精确的说法,b
匹配这样的位置:它的前⼀个字符和后⼀个字符不全是(⼀个是,⼀个不是或不存在) w
举个例子
bw{6}b
匹配 刚好6个字符的单词
^d{5,10}$
匹配你在表单里填写的 5 到 10 位的 QQ 号
重复匹配
上面给的例子中就已经给出了重复的概念了,有了重复的概念,正则表达式才更加简洁高效,下面是一些有关重复的限定符
限定符 | 意义 |
---|---|
* | 重复 0 次或更多次 |
| 重复 1 次或更多次 |
? | 重复 0 次或 1 次 |
{n} | 重复 n 次 |
{n,} | 重复 n 次或更多次 |
{n, m} | 重复 n 到 m 次 |
字符匹配
前面已经匹配过数字、空白、字母了,如果想自己定义一个集合取匹配呢,这时要用到我们另外一个元字符 [] 了,它匹配的是一个字符
元字符 | 意义 |
---|---|
[0-9] | 匹配数字,相当于 d |
[.?!] | 匹配 . 或 ? 或 ! |
[a-z0-9A-Z] | 匹配字母、数字、下划线,相当于 w (如果没有中文的情况下) |
[^0-9] | 匹配除数字外的任意字符,相当于 [^d] |
注意 [] 里不用加入空格,否则会把空格给匹配,出现元字符也不用转义,因为它们此时代表的就是普通的字符
举个例子
(?0d{2}[) -]?d{8}
就可以匹配 (010)88886666
和 022-22334455
以及 02912345678
这些格式的号码。
逻辑分支条件
有时候我们的正则表达式可能会匹配到我们不想要的数据,比如上面那个例子就会匹配到 ` 010)12345678 ` 以及 (022-87654321
这样格式的数据,要解决这个问题我们可以用分支条件( 也就是逻辑或 |
)写多一点表达式,只要满足其中的一项就成功匹配到。
举个例子
0d{2}-d{8}|0d{3}-d{7}
这个表达式能匹配两种以连字号分隔的电话号码:
- 三位区号,如 010-12345678
- 四位区号,如 0376-2233445
#分支条件也有短路效应,只要匹配了左边的表达式就不会再去匹配右边的,所以写的时候要注意顺序。
分组
之前已经介绍了单个字符的重复,如果想让多个字符重复的话,我们可以用 ()
将想要重复的字符括起来让它变成一个分组或者子表达式,然后在括号后面就可以像之前那样用重复限定符了。
举个例子
` ((2[0- 4]d|25[0-5]|[01]?dd?).){3}(2[0-4]d|25[0-5]|[01]? dd?) 用来匹配 ip 地址,其实就是重复了 3 次
2[0-4]d|25[0-5]|[01]?dd?` 表达式,不难分析。
后向引用
前面用的 ()
实现了多个字符的重复,直接紧跟在后面加上限定符就行了,如果我们不想重复匹配多次,而是要在后面引用这次匹配到的内容该怎么办呢,我们可以用后向引用
每次用 ()
进行一次分组时,()
里的内容都会拥有一个分组,从 1 开始一直递增,第 0 个分组是整个正则表达式本身,所以 1
就表示重复一次第一个分组捕获到的内容。
举个例子
b(w )bs 1b
可以用来匹配重复的单词,如 so so
,b(w )b
表示匹配一个单词,(w )
就是分组 1 , s
表示一个或多个空白符,紧接着就是 1
,也就是上次匹配到的单词,然后就以这个单词结束。
零宽断言
零宽断言分为后行断言和先行断言,它们是特殊类型的非捕获组 (也就是说匹配的不是自己,是别人),因为只匹配模式,不占字符,所以叫做零宽。当我们在一种特定模式之前或者之后有这种模式时,会优先使用断言(尤其是匹配 HTML 元素时)。
举个例子
我们想获取输入字符串 4.44 and 10.88 中 字符之后的所有数字。我们可以使用这个正则表达式 (?<=
断言模式 | 意义 |
---|---|
(?=exp) | 正向先行断言(positive lookhead),断⾔⾃⾝出现的位置的后⾯能匹配表达式exp |
(?<=exp) | 正向后行断言(positive lookbehind),断⾔⾃⾝出现的位置的前⾯能匹配表达式exp |
(?!exp) | 负向先行断言(negative lookhead), 断⾔此位置的后⾯不能匹配表达式exp |
(?<!exp) | 负向后行断言(negative lookbehind),断⾔此位置的前⾯不能匹配表达式exp |
举个例子
代码语言:javascript复制// positive lookhead
`sinM.`.match(/sin(?=M.)/g); // ["sin"]
`M.sin`.match(/sin(?=M.)/g); // null
// positive lookbehind
'sinM.'.match(/(?<=M.)sin/g); // null
'M.sin'.match(/(?<=M.)sin/g); // ["sin"]
// negative lookhead
`M.sin`.match(/sin(?!M.)/g); // ["sin"]
`sinM.`.match(/sin(?!M.)/g); // null
// negative lookbehind
'sinM.'.match(/(?<!M.)sin/g); // ["sin"]
'M.sin'.match(/(?<!M.)sin/g); // null
再举个例子
` (?<=<(w )>).*(?=</1>) 匹配 不包含属性的简单HTML标签内⾥的内容,好好思考一下,上面这个表达式可以将
<p>RE</p>` 中的 RE 给匹配出来。
贪婪与懒惰匹配
正则表达式跟人一样,都是贪婪的,所以当有可重复的限定符时,正则表达式会匹配最长的那个结果,有时我们不想让他变得那么贪婪,就可以用懒惰匹配,也就是在限定符后面加个 ?
限定符 | 意义 |
---|---|
*? | 重复任意次,但尽可能少重复 |
? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
处理标记
标记 | 描述 |
---|---|
i | 不区分大小写: 将匹配设置为不区分大小写。 |
g | 全局搜索: 搜索整个输入字符串中的所有匹配。 |
m | 多行匹配: 会匹配输入字符串每一行。 |
"/.at(.)?$/" => The fat
cat sat
on the mat.
// 这样只会匹配 mat
"/.at(.)?$/gm" => The fat
cat sat
on the mat.
// 这样会匹配 fat sat mat
参考文章 1 参考文章 2 参考文章