正则表达式
最近一直在忙于加班,已经连续两个星期996(9点上班9点下班星期六加班)了!
项目采用的是 TDD 开发模式, 所以前期写测试写到吐(还好已经熬过这段时间了)。不过确实在写接口的时候有了这些测试方便了许多。
咦,总感觉跑题了!
进入正题,之前一直想学 正则表达式 ,一直没学(因为任性),趁刚写完一个接口还有时间喘气的时候把它给学了吧!
what is 正则?
正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。
先来几个简单的正则!
假设要在一篇文章中找 ‘hi’ 这个单词。那就直接用正则表达式
hi
。 它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配 ‘hi,HI,Hi,hI’ 这四种情况中的任意一种。但是这时候很多有 ‘hi’ 的单词也会被找到,比方说 ‘high’,‘him’等等,如果要精确查找, 可以用
\bhi\b
。
\b
是正则表达式规定的一个特殊代码(元字符,metacharacter),能匹配一个单词边界,也就是指单词和空格间的位置。例如,er\b
可以匹配”never” 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。用了\ber\b
后就只能匹配单词 ‘er’
\bhi\b.*\bmiao\b
: 匹配的字符串是:先是一个单词 hi ,然后是任意个任意字符(但不能是换行),最后是 miao 这个单词。
.
的作用是匹配除 “\n” 之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,使用[.\n]
的模式。
*
它代表的不是字符,也不是位置,而是数量。它指定 * 前边的内容可以连续重复使用任意次以使整个表达式得到匹配。
元字符
看了上面的几个例子,估计已经大概明白 元字符
是个什么东西了吧。接下来介绍几个基本的元字符
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
元字符 ^
和 $
都匹配一个位置,这和 \b
有点类似。^
匹配用来查找的字符串的开头,$
匹配结尾。比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$
。
{5,12}
是重复的次数不能少于5次,不能多于12次的意思,否则都不匹配。因为使用了 ^
和 $
,所以输入的整个字符串都要用来和 \d{5,12}
来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。
和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^
和 $
的意义就变成了匹配行的开始处和结束处。
字符转义
如果想查找元字符本身的话,比如(.
和 *
), 就需要使用 \
来取消这些字符的特殊意义。例如应该使用 \.
和 \*
。当然,要查找 \
本身,得用 \\
.
例如:f10\.moe
匹配 f10.moe,C:\\Windows
匹配 C:\Windows。
限定符
正则表达式中所有的限定符(指定数量的代码,例如*,{5,12}等):
代码/语法 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
字符类
要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母 a,e,i,o,u
),应该怎么办?
很简单,你只需要在方括号里列出它们就行了,像 [aeiou]
就匹配任何一个英文元音字母,[.?!]
匹配标点符号(.
或?
或!
)。
我们也可以轻松地指定一个字符范围,像 [0-9]
代表的含意与 \d
就是完全一致的:一位数字;同理 [a-z0-9A-Z_]
也完全等同于 \w
(如果只考虑英文的话)。
下面是一个更复杂的表达式: \(?0\d{2}[) -]?\d{8}
。(
和 )
也是元字符,所以在这里需要使用转义。 这个表达式可以匹配几种格式的电话号码,像 (010)88886666
,或 022-22334455
,或 02912345678
等。
解析:
- 转义字符
\(
,它能出现 0 次或 1 次(?)
- 然后是一个 0,后面跟着 2 个数字
(\d{2})
- 然后是
)
或-
或空格
中的一个,它出现 1 次或不出现(?)
- 最后是 8 个数字
(\d{8})
。
分枝条件
可是, 刚才那个表达式也能匹配 010)12345678 或 (022-87654321 这样的 “不正确” 的格式。要解决这个问题,我们需要用到 分枝条件 。正则表达式里的 分枝条件 指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用 “|
“ 把不同的规则分隔开。
0\d{2}-\d{8}|0\d{3}-\d{7}
可以匹配 010-12345678 或者 0111-1234567 这两种电话号码。\(?0\d{2}\)?[- ]?\d{8}|0\d{2}[- ]?\d{8}
这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。\d{5}-\d{4}|\d{5}
这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的 顺序 。如果你把它改成\d{5}|\d{5}-\d{4}
的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会 从左到右 地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。
分组
上面讲了要重复单个字符时只需在后面加限定符就行,多个字符需要重复时可以用 小括号 来指定 子表达式 (也叫分组)。
例如:
(\d{1,3}\.){3}\d{1,3}
是一个简单的IP地址匹配表达式。\d{1,3}
匹配 1 到 3 位的数字,(\d{1,3}\.){3}
匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复 3 次,最后再加上一个一到三位的数字(\d{1,3})
。不幸的是,它也将匹配 256.300.888.999 这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:
((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
。
反义
有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义:
代码/语法 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下划线,汉字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非数字的字符 |
[^x] | 匹配除了x以外的任意字符 |
[^aeiou] | 匹配除了aeiou这几个字母以外的任意字符 |
正则入门算是差不多了,要了解更多可以点下面链接深入:
link
http://deerchao.net/tutorials/regex/regex.htm