正则表达式就是记录文本规则的代码。只不过比起通配符,它能更精确地描述你的需求
1、元字符
常用的元字符
代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字,,等价于[a-z0-9A-Z_](如果只考虑英文的话)
\s 匹配任意的空白符
\d 匹配数字,等价于[0-9]
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束
2、重复(限定符)
常用的限定符
代码/语法 说明
* 重复零次或更多次,任意数量的不包含换行的字符
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
懒惰限定符
代码/语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
例子
1、精确地查找hi这个单词
\bhi\b
2、先是单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词
\bhi\b.*\bLucy\b
3、以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字
0\d{2}-\d{8} \d必须连续重复匹配2次(8次)
4、匹配刚好6个字符的单词
\b\w{6}\b
5、QQ号必须为5位到12位数字
\d{5,12}\d
6、匹配最长的以a开始,以b结束的字符串,称为贪婪匹配。
a.*b
7、匹配最短的,以a开始,以b结束的字符串
a.*?b
8、
\(?0\d{2}[) -]?\d{8}
解析:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)或-或空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。
匹配到不符合的:(012-90998908或者010)19092345
修改
\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}
9、
[a-z]是指小写字母中的一个字符
[A-Z]是指大写字母中的一个字符
[0-9]是指数字中的一个字符
[a-zA-Z0-9_-]是指大小写字母、数字、下划线、横线中的一个字符
[a-z][A-Z][0-9]是指三个字符,其中第一个是小写字母,第二个是大写字母,第三个是数字
3、字符转义
\ 取消元字符的特殊意义
要查找\本身,用\\
要查找*本身,用\*
要查找(本身,用\(
4、反义:查找不属于某个能简单定义的字符类的字符
常用的反义代码
代码/语法 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的任意字符
例子:
匹配用尖括号括起来的以a开头的字符串
<a[^>]+>
注意:<a+>会匹配到<aaa>like>
5、分枝条件
满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开,从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了,注意顺序
美国邮编的规则是5位数字,或者用连字号间隔的9位数字
\d{5}-\d{4}|\d{5}
只会匹配5位的邮编(以及9位邮编的前5位)
\d{5}|\d{5}-\d{4}
6、分组
重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作
默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
常用分组语法
分类 代码/语法 说明
捕获 (exp) 匹配exp,并捕获文本到自动命名的组里
(?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号
零宽断言 (?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
注释 (?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读
简单的IP地址匹配
(\d{1,3}\.){3}\d{1,3}
分析:\d{1,3}匹配1到3位的数字,(\d{1,3}\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。匹配到不符合的260.333.222.999
使用冗长的分组
描述一个正确的IP地址:255.255.255.255
((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
25[0-5] 代表250~255范围
2[0-4]\d 代表240~249范围
[01]\d\d? 代表0~9或10~19或100~199的范围
语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。
7、反向引用
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本
匹配重复的单词,像go go
\b(\w+)\b\s+\1\b
指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成'也行:(?'Word'\w+))
反向引用这个分组捕获的内容,你可以使用\k
匹配重复的单词,像go go
\b(?<Word>\w+)\b\s+\k<Word>\b。
8、零宽断言
用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言
(?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
匹配以'a'间隔的数字(再次强调,不包括'a'字符)
匹配a字母前面两个数字
\d+(?=\a)
9、负向零宽断言
查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
\b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。
匹配除a字母外前面两个数字,如11b,22c等
\d{2}(?!a)
补充:
(?:exp) 非捕获组,匹配exp的内容,但不捕获到组里
至于作用,一般来说是为了节省资源,提高效率
比如说验证输入是否为整数,可以这样写
^([1-9][0-9]*|0)$
这时候我们需要用到()来限制“|”表示“或”关系的范围,但我们只是要判断规则,没必要把exp匹配的内容保存到组里,这时就可以用非捕获组了
^(?:[1-9][0-9]*|0)$
以上只是一个简单的例子,或许会觉得无所谓,但是如下的日期正则,如果全都用捕获组,那就是对资源的极大浪费了
^(?:(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))([-/.])(?:0?2\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})([-/.])(?:(?:(?:0?[13578]|1[02])\2(?:31))|(?:(?:0?[13-9]|1[0-2])\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\2(?:0?[1-9]|1\d|2[0-8]))))$