本文内容是对书籍《正则表达式必知必会》的提炼
正则表达式
用来匹配和处理文本的字符串,主要功能是查找和替换。
正则的语法简单,难点在于使用这些语法,学习方法是多实践。
纯文本
Ben
匹配Ben这个文本,注意区分大小写,Ben和ben是不一样的
英文句号,匹配任意一个字符
salsa.
可以匹配,salsa1或者salsa2
.a.
可以匹配,aaa或者1ab
有一个英文句号不能匹配,那就是换行符
反斜杠,匹配特殊字符
如果表达式中需要的就是一个英文句号,就在句号前面加一个反斜杠
.a.\.
可以匹配,aaa. 或者 1ab.
要匹配反斜杠,就在前面在加个反斜杠
匹配多个字符中的一个
[ns]a\.xls
可以匹配,na.xls或者sa.xls,但是不会匹配 aa.xls
[0-9] = [0123456789]
[a-z] = [abcd….xyz]
[A-Z0-9a-z] = [ABC…XYZ012…789abc…xyz]
取非匹配
除了这些字符,其他的都可以匹配
[ns]a[^0-9]\.xls
可以匹配 naa.xls,不能匹配 na0.xls
空白元字符
元字符 | 说明 |
[\b] | 回退一个字符 |
\f | 换页符 |
\n | 换行符 |
\r | 回车符 |
\t | 制表符 |
\v | 垂直制表符 |
在windows里面\r\n匹配一个换行,但是unix系统中\n就可以匹配换行
匹配特定的字符类别
匹配数字
元字符 | 说明 |
\d | 任何一个数字字符,等价于[0-9] |
\D | 任何一个非数字字符,等价于[^0-9] |
匹配字母和数字
元字符 | 说明 |
\w | 任何一个字母(大小写均可)数字字符或下划线,等价于[a-zA-Z0-9_] |
\W | 任何一个非字母数字字符和下划线,等价于[^a-zA-Z0-9_] |
匹配空白字符
元字符 | 说明 |
\s | 任何一个空白字符,等价于[\f\n\t\r\v] |
\S | 任何一个非空白字符,等价于[^\f\n\t\r\v] |
使用POSIX字符类
许多正则表达式都支持POSIX字符类
重复匹配
正则表达式的真正威力所在就是重复匹配
+号,匹配一个或者多个字符
问题,如何写一个邮箱匹配模式
\w@\w\.\w
可是这个只能匹配 a@b.c 这种,不能匹配 b@forta.com 和 how@forta.com
使用+号就可以实现这个功能
\w+@\w+\.\w+
但是我们发现,这个模式无法匹配 ben.forta@forta.com 和 ben@forta.help.com
我们可以使用+号来匹配字符集
[\w.]+@[\w.]+\.\w+
最后一个+号没有匹配字符集的原因是那样会匹配 ben.forta@forta.com. 这种最后有英文句号的情况
但是我们这个模式可以匹配邮箱最前面有英文句号的情况 .ben.forta@forta.com
注意:在字符集合里面,特殊字符可以不用转义,但是也可以转义,[\w.] == [\w\.]
*号,匹配0个或多个字符
*号和+号类似,只是+号至少匹配一个,而*号匹配可有可无的字符或字符集
利用*号,我们可以解决上面的问题,先用\w+匹配第一个字符,然后用[\w.]*匹配@之前,第一个字符之后的字符
\w+[\w.]*@[\w.]+\.\w+
疑问:去掉第一个加号,答案也是对的吧? \w[\w.]*@[\w.]+\.\w+
解答:答案也是对的,但是第一种在语义上更容易理解
?号,匹配0个或一个字符
假设我们要匹配网址URL
http://[\w./]+
可以匹配 http://forta.com 但是不能匹配 https://forta.com
修改成
https?://[\w./]+
即可
{}花括号,匹配的重复次数
?号,*号和+号都不能限制匹配的确定次数,比如我想只匹配6次,这就需要使用花括号
问题:匹配十六进制颜色码,需要匹配#034EFD,#FFFFFF这样的字符串
解法1:
#[0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]
解法2:
#[0-9a-fA-F]{6}
匹配的区间
如果要匹配至少多少次,最多多少次呢,可以使用{2, 4},这样表示至少匹配2次,最多匹配4次
如果只说明至少匹配多少次,没说最多匹配的次数,使用{2, },省略了4,表示至少匹配2次,最多无限次
防止过度匹配
例子:如果我们要匹配网页标签,比如<B>,应该如何匹配
<Bb>.*</Bb>
如果文本是这样的,<B>ak</B> and <B>hk</B>,那么这个模式会将其全部匹配,只得到一条结果,而不是两个结果,因为*,+这些符号默认是贪婪匹配,对于一个结果,它会一直找到文档末尾,所以需要防止过度匹配的时候,要使用这些字符的懒惰版本
贪婪性元字符 | 懒惰性元字符 |
* | *? |
+ | +? |
{n, } | {n, }? |
懒惰匹配版本模式
<Bb>.*?</Bb>
这样便可以解决问题
位置匹配
\b,单词边界
边界匹配用来解决在什么地方进行字符串匹配操作的问题。
文本:the cat scattered his food all over the room.
模式:
cat
结果:the cat scattered his food all over the room.
\b用来匹配单词的开始和结尾,它匹配的是这样一个位置,这个位置位于一个能够用来构成单词的字符(\w:字母,数字和下划线)和一个不能用来构成单词的字符(\W)之间。
所以使用
\bcat\b
将只匹配第一个cat
如果想要匹配第二个cat呢?使用\B
\Bcat\B
字符串匹配,^(字符串开头) 和 &(字符串结尾)
例子:匹配XML文档
<\?xml.*\?>
可以匹配 <?xml version=”1.0”?>,但是如果这个文本出现在文档的第二行,那这就不是一个xml文档
^<\?xml.*\?>
只要在模式前面加上^,表示匹配字符串开头,就可以解决这个问题
例子:匹配web页面的</html>标签,这个标签后面不应该有任何东西
</[Hh][Tt][Mm][Ll]>&
(?m),分行模式
启用分行模式之后,^和&将会匹配每一行的开头和结尾
(?m)必须出现在文档的最前面
(),子表达式
例子1:在网页开发中,经常会使用 这个来表示空格,比如 Windows 2000,显示出来是Windows 2000,
{2, }
0在上面这个例子中,我们试图使用匹配 ,但是因为元字符只是对前一个字符有效,所有我们只能匹配到类似&bnsp;;;;;;;这样的字符串
解决办法,使用子表达式
( ){2, }
例子2:匹配IP地址
\d{1, 3}\.\d{1, 3}\.\d{1, 3}\.\d{1, 3}
可以匹配,10.0.0.55 这样的IP地址
使用子表达式
(\d{1, 3}\.){3}\d{1, 3}
字表达式的嵌套
例子3:准确IP地址匹配
在例子2中,我们匹配的IP地址模式存在一定的问题,因为IP地址每个数字最大不超过255,可是我们的模式可以匹配999这样的数,在正则表达式中,我们需要定义清除想要什么,不想要什么。
- 任何一个1位或2位的数字
- 任何一个以1开头的3位数字
- 任何一个以2开头,第2位数据在0~4之间的3位数字
- 任何一个以25开头,第三位数字在0-5之间的3位数字
(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5])))
在这个模式中,| 表示或的意思,在分号的左边和右边最好使用子表达式括起来
回溯引用,前后一致匹配
回溯引用的意思是,模式的后面一部分引用了前面的字表达式。使用\1 ~ \n 来引用前面的子表达式,第一个字表达式就是\1,第二个是\2,依次类推。
例子1:找到网页中所有的标题标签,从h1-h6
模式:
<[Hh][1-6]>.*?</[Hh][1-6]>
这个模式可以匹配到所有的h1-h6,并且是懒惰式搜索,但是有一个问题,就是此模式可以匹配到<h2>..</h3>这样的非法标签,如何解决呢,这就需要使用回溯引用了。
模式:
<([Hh][1-6])>.*?</\1>
将 [Hh][1-6] 变成一个子表达式,然后再后面用 \1 引用,这样\1能匹配的值就和之前的表达式一样,就不会出现匹配到<h2>..</h3>的问题。
例子2:有如下一段文本,找到文本中连续重复出现的单词
This is a block of of text.
Several words here are are repeated, and and they should not be.
模式:
[\s]+(\w+)[\s]\1
前后查找
例子1:找到网页文档中<title></title>中包含的内容
模式:
<title>.*?</title>
可以匹配,<title>hello world</title>,但是我们需要的是内容,而不需要<title>这个标签,找到之后还要用代码进行提取,比较麻烦。正则可以实现这个需求,也就是让匹配的文本不出现在结果中。
(?=), 向前查找
后面的匹配结果不出现在结果中。
例子2:提取URL的协议名
模式:
.*:
匹配结果:http: , ftp: , https: ,虽然结果基本正确,但是我们并不需要用来匹配的冒号:,因为冒号在最后面,所以我们使用向前查找,语法是,?=
模式:
.*(?=:)
结果中就不包含冒号了,需要注意的地方是,?=必须用字表达式括起来。
(?<=)向后查找
和向前查找类似,只是前面的匹配字符不会出现在结果中。
ABC01: $23.45
HGG42: $5.32
BAIDU: $0.23
例子:提取出数据库中的价格。
模式:
\$[0-9.]+
可以匹配到正确的结果,但是我们不需要价格前面的$符号,可以使用向后查找
(?<=\$)[0-9.]+
注意:向前查找所查找的字符串长度是可变的,可以和英文句号,+号之类的元字符结合起来,但是向后查找只能是固定长度的字符串
对前后查找取非(略)
比较少用,不学了