给忙碌人的正则表达式
前言
正则表达式看了忘忘了又看,干脆写个文章做个记录,后续也可以不断补充扩展
什么是正则表达式
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
- 灵活性、逻辑性和功能性非常强
- 可以迅速地用极简单的方式达到字符串的复杂控制
- 对于刚接触的人来说,比较晦涩难懂
为什么用正则表达式
工作中查看文档或者日志的时候经常会用到搜索,很多时候是通过指定的字符串直接搜索,但是有时候会遇到比较复杂的搜索条件,一个固定的字符串往往不能搜索到所有想要的数据,比如要从日志中搜索138开头的手机号,如果仅仅搜索138这个固定的字符串,那么很可能会得到大量不需要的数据,在这种情况下正则表达式就是最好选择。
在线测试
初学者或者在工作中用到正则表达式可以去一些在线网站去测试自己写的表达式:
regular expression 101
菜鸟工具
bejson
正则表达式使用
常用元字符
语法 | 说明 | 示例 |
---|---|---|
. | 匹配除换行符以外的任意字符 | 规则 a.c 可以匹配到 abcd 中的 abc |
\w | 匹配字母或数字或下划线或汉字 | 规则 a\wc 可以匹配到 abcd 中的 abc |
\s | 匹配任意的空白符 | 规则 a\sc 可以匹配到 a cd 中的 a c |
\d | 匹配数字 | 规则 a\dc 可以匹配到 a2cd 中的 a2c |
\b | 匹配单词的开始或结束 | 规则 \babc\b 可以匹配到 abc def 中的 abc |
^ | 匹配字符串的开始 | 规则 ^abc 可以匹配到 abcabc 中前面那个 abc |
$ | 匹配字符串的结束 | 规则 abc$ 可以匹配到 abcabc 中后面那个 abc |
反义
查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义
语法 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下划线的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非数字的字符 |
\B | 匹配不是单词开头或结束的位置 |
[^abcdef] | 匹配除了abcdef这几个字母以外的任意字符 |
字符转义
如果需要查找元字符本身的话就出现了问题:没办法指定它们,因为它们会被解释成规则。这时就得使用 \ (反斜杠) 来取消这些字符的特殊意义。要查找 \ 本身,你也得用 \\
例如:baidu\.com匹配baidu.com,D:\\Test匹配D:\Test
重复
语法 | 说明 | 示例 |
---|---|---|
* | 重复零次或更多次 | abc\d* 可以匹配 abc1、abc、abc123 |
+ | 重复一次或更多次 | abc\d+ 可以匹配 abc1和abc123,不能匹配abc |
? | 重复零次或一次 | abc\d? 可以匹配 abc1和abc,不能匹配abc123 |
{n} | 重复n次 | abc\d{2} 可以匹配 abc12和abc23,不能匹配abc1或者abc123 |
{n,m} | 重复n到m次 | abc\d{2,3} 可以匹配 abc12和abc123,不能匹配abc1或者abc1234 |
多规则匹配
语法 | 说明 |
---|---|
| | 分割多个规则,满足其中任意一种规则就算匹配 |
- x|abcd 能匹配 x 或 abcd。(a|x)bcd 则匹配 abcd 或 xbcd。
- 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地址匹配表达式,先匹配1到3个数字(\d{1,3})以及一个点,然后重复这个模式3次,最后在匹配1到3个数字。
((2[0-4]\d|25[0-5]|[01]?\d\d?).){3}(2[0-4]\d|25[0-5]|[01]?\d\d?):这是正确的IP匹配规则,主要是要理解每个数字不能大于255
向后引用
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
- 分组0对应整个正则表达式
- 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配
语法 | 说明 |
---|---|
(exp) | 匹配exp,并捕获文本到自动命名的组里 |
(?<name>exp) | 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name’exp) |
(:exp) | 匹配exp,不捕获匹配的文本,也不给此分组分配组号 |
\b(\w+)\b\s+\1\b可以用来匹配重复的单词,像ha ha, 或者cat cat。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)
也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?\w+)(或者把尖括号换成’也行:(?‘Word’\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k,所以上一个例子也可以写成这样:\b(?\w+)\b\s+\k\b
注释
小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)
贪婪与懒惰
语法 | 说明 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样 .*? 就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复
a.*?b 匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权
零宽断言
这玩意不是每个正则表达式工具都能支持的,如果使用grep工具需要加上-P参数
代码 | 名称 | 说明 | 示例 |
---|---|---|---|
(?=exp) | 零宽度正预测先行断言 | 判断自身出现的位置的后面能匹配表达式exp | 规则a(?=b) 匹配后面有 b 的 a 对于abxoxcd 规则(?=xox)…匹配 xo 规则\b\w+(?=ing\b)匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I’m singing while you’re dancing.时,它会匹配sing和danc |
(?<=exp) | 零宽度正回顾后发断言 | 判断自身出现的位置的前面能匹配表达式exp | 规则(?<=a) b 匹配前面有 a 的 b 对于abxoxcd 规则(?<=xox)..匹配 cd 规则(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading |
(?!exp) | 零宽度负预测先行断言 | 判断此位置的后面不能匹配表达式exp | 规则a(?!b) 匹配后面没有 b 的 a 规则\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字 |
(?<!exp) | 零宽度负回顾后发断言 | 判断此位置的前面不能匹配表达式exp | 规则(?<!a) b 匹配前面没有 a 的 b 规则(?<![a-z])\d{7}匹配前面不是小写字母的七位数字 |
是不是觉得这些术语名称太复杂,太难记了?我也有同感。知道有这么一种东西就行了,它叫什么,随它去吧!人若无名,便可专心练剑;物若无名,便可随意取舍……
简单来说零宽断言可以去掉你不关心的部分内容,比如获取机器ip地址
[root@home64 ~]# ifconfig eth1
eth1 Link encap:Ethernet HWaddr 52:54:00:C9:53:EC
inet addr:192.168.18.18 Bcast:192.168.18.255 Mask:255.255.255.0
inet6 addr: fe80::5054:ff:fec9:53ec/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:4695963 errors:0 dropped:0 overruns:0 frame:0
TX packets:479796 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1194408667 (1.1 GiB) TX bytes:110150827 (105.0 MiB)
[root@home64 ~]# ifconfig eth1 | grep -oP 'addr:[\d\.]+'
addr:192.168.18.18
[root@home64 ~]# ifconfig eth1 | grep -oP '(?<=addr:)[\d\.]+'
192.168.18.18
[root@home64 ~]# ifconfig eth1 | grep -oP "[0-9.]+(?= Bcast)"
192.168.18.18
处理选项
这些选项得看使用的正则表达式工具是否支持
名称 | 说明 |
---|---|
IgnoreCase(忽略大小写) | 匹配时不区分大小写 |
Multiline(多行模式) | 更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下 $ 的精确含意是:匹配\n之前的位置以及字符串结束前的位置) |
Singleline(单行模式) | 更改 . 的含义,使它与每一个字符匹配(包括换行符\n) |
IgnorePatternWhitespace(忽略空白) | 忽略表达式中的非转义空白并启用由#标记的注释 |
ExplicitCapture(显式捕获) | 仅捕获已被显式命名的组 |