正则表达式是什么
正则表达式(Regular expression,简写为Regexes或Regex)是一种用来操作和检验字符串数据的强大工具。
如果你用过windows的文件查找,或者一些文本编辑器的查找功能,你可能知道通配符,比如*以及?。正则表达式和通配符类似,也是用来进行文本匹配的工具,只不过它比通配符更加精确,也更加复杂。它相当与一串特殊的字符,用它可以转换成算法,对文本进行匹配等操作。
入门
事实上正则表达式有其自身的一套语法,这种语法对于初学者来说显得有些晦涩难懂。尤其是其构造比较困难,成为很多入门者的障碍。所以学习正则表达式最好的办法是先从例子入手,理解了例子以后再对例子进行修改、实验,再去了解更多的语法,从而能够理解或写出更加复杂的正则表达式。
如果你在一段文本里想要查找 hi ,那么你可以使用正则表达式hi
。
没错,这也算是正则表达式,不过是最简单的一种,他可以找出 hi ,但是他也会找出 hide 里面的 hi 。如果你想要匹配的是单词 hi ,那么你要使用\bhi\b
。这样, hide 不会被找到,而 hi,de 则会被找到。
\b
就是正则表达式里的元字符(metacharacter),它代表单词的开头或结尾,即单词的分界处。它匹配了一个位置,而非某个字符。
如果你想要找 synchronized ,但是又记不清楚怎么拼了,只记得前面是 syn ,后面是 nized ,那么正则表达式可以这么写syn.*nized
。
这里,点号是另一个元字符,匹配除了换行符以外的任意字符。星号同样是元字符,不过它代表的不是字符,也不是位置,而是数量,它指定星号前边的内容可以连续重复使用任意次以使整个表达式得到匹配。那么syn.*nized
的意思就是:先是一个 syn ,然后是任意个任意字符(但不能是换行),最后是 nized 。
如果想要找的 syn 和 nized 中间我们知道是两个数字,如syn12nized,那么表达式这么写syn\d\dnized
,\d
也是个元字符,匹配一个数字。
刚才我们已经见识到了正则表达式,下面我们来看更多的例子。
0\d\d-\d\d\d\d\d\d\d\d
,匹配这样的字符串:以0开头,后面是两个数字,然后一个-(减号,或者叫中划线),然后8个数字。这个表达式也可以这么写:0\d{2}-\d{8}
,这里的{2}表示它前面的\d要重复2次。
\d+
,匹配1个或更多连续的数字。这里的加好是和星号类似的元字符,不同的是星号匹配重复任意次(可能是0次),而加好则匹配重复1次或更多次。
\b\w{6}\b
,匹配刚好6个字符的单词。
^\d{2,5}$
,匹配整个字符串为2-5个数字的字符串。元字符^
(和数字6在同一个键位上的符号)和$
都匹配一个位置,这和\b
有点类似。^
匹配你要用来查找的字符串的开头,$
匹配结尾。{2,5}和前面介绍的{2}类似,只不过{2}匹配重复两次,而{2,5}匹配重复2次到5次。
常用规则
常用的匹配符
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\s | 匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等 |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
[x] | 匹配x字符 |
[^abc] | 匹配abc这几个字符 |
常用的限定符
代码 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
常用的反义匹配符
代码 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下划线,汉字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非数字的字符 |
\B | 匹配不是单词开头或结束的位置 |
[^x] | 匹配除了x以外的任意字符 |
[^abc] | 匹配除了abc这几个字母以外的任意字符 |
转义符
转义符\ 用来消除字符的特殊意义,如\.
可以匹配.字符本身,\\
可以匹配\字符本身 。
分枝条件
正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。
比如你要匹配2,你就直接写表达式2
,但是你如果要匹配2或者3,你就要用分枝条件了,2|3
。
看一个复杂一点的例子,比如\d{5}-\d{4}|\d{5}
,这个表达式用于匹配美国的邮政编码,它可以匹配5位数字,或者用连字号间隔的9位数字。但是要注意,使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}
的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。这个类似于java的短路原则。
分组
分组也就是用小括号来指定子表达式。
比如你要重复一个数字2,重复3遍,你可以用2{3}
,但你要重复25,你就要用分组了,(25){3}
。
看一个复杂一点的例子,比如(\d{1,3}\.){3}\d{1,3}
,这个表达式可以匹配IP地址格式的字符串。不过他也能匹配到999.999.999.999这样的字符串,而IP地址中每个数字都不能大于255,所以要匹配IP地址的话还要复杂的多: ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
。
贪婪与懒惰
如果你写一个表达式a.*b
,这表示匹配a开头b结尾的字符串,但是你写一个字符串,11aaabbb11,你会发现,他匹配到的是aaabbb。也许你会有疑问,aaabbb确实满足条件,但是aaab也满足条件啊,为什么匹配出来的是aaabbb而不是aaab?
当正则表达式中包含能接受重复的限定符时,通常的行为是,在使整个表达式能得到匹配的前提下,匹配尽可能多的字符。这被称为贪婪匹配。
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。
如果你刚才想要匹配出来的是aaab的话,表达式就这么写a.*?b
。
懒惰限定符
代码 | 说明 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
常用正则表达式
下面收集了一些比较常用的正则表达式。
网址(URL): [a-zA-z]+://[^\s]*
IP地址: ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
电子邮件: \w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
QQ号码: [1-9]\d{4,}
HTML标记(包含内容或自闭合): <(.*)(.*)>.*<\/\1>|<(.*) \/>
日期(年-月-日): (\d{4}|\d{2})-((1[0-2])|(0?[1-9]))-(([12][0-9])|(3[01])|(0?[1-9]))
日期(月/日/年): ((1[0-2])|(0?[1-9]))/(([12][0-9])|(3[01])|(0?[1-9]))/(\d{4}|\d{2})
时间(小时:分钟, 24小时制): ((1|0?)[0-9]|2[0-3]):([0-5][0-9])
汉字(字符): [\u4e00-\u9fa5]
中文及全角标点符号(字符): [\u3000-\u301e\ufe10-\ufe19\ufe30-\ufe44\ufe50-\ufe6b\uff01-\uffee]
中国大陆固定电话号码: (\d{4}-|\d{3}-)?(\d{8}|\d{7})
中国大陆手机号码: 1\d{10}
中国大陆邮政编码: [1-9]\d{5}
中国大陆身份证号(15位或18位): \d{15}(\d\d[0-9xX])?
非负整数(正整数或零): \d+
负整数: -[0-9]*[1-9][0-9]*
整数: -?\d+
小数: (-?\d+)(\.\d+)?
不包含abc的单词: \b((?!abc)\w)+\b
用户名: ^[a-z0-9_-]{3,16}$
密码: ^[a-z0-9_-]{6,18}$
更多内容
以上我们介绍了正则表达式的一些基础规则以及常用正则表达式,正则表达式还有很多比较复杂的高级应用,不过我们吃透了这些基础,日常的应用查不多可以应对了。更多内容,后续补充。