这是一篇针对正则表达式的完整介绍,将会分为两个部分,第一部分包含平常使用正则会高频率用到的正则语法,第二部分包含一些进阶语法。正则表达式
不少软件都提供了文本的查找功能,一般使用 Ctrl+F/Command+F 快捷键来使用,可是通常这些软件提供的文本查找功能都很是基础,只能进行全字符匹配,好比输入“Hello”就只能匹配到“Hello”,而要按照一些特定规则查找的话,就没法实现了。正则表达式是一个用来查找匹配特定文本的工具,在软件开发过程当中会常用到,它能够按照特定的规则进行文本的查找匹配。编程
正则一般使用一对斜杠 /
来标记,好比 /^Hello World$/
,可是并非全部地方都是使用 /
来标记的,只不过对于绝大多数编程语言来讲是这样的,或者说,先后两个 /
并不属于正则,只有中间的部分才是。本文也将采起通常的形式,使用 /
来标记正则表达式。json
正则做为一个“高级”文本查找工具,对于通常软件的基本全字匹配功能确定是涵盖在内的。要实现普通的全字匹配,只须要和普通的查找同样,直接写出要匹配的字符串便可,可是因为正则表达式有一些特殊的语法,所以有些符号是有特殊用处的,包括:\
、^
、$
、|
、.
、+
、*
、{
、?
、(
、)
、[
等,这些特殊符号的含义将在后面讲到,对于这些特殊符号,若是须要用到的话,须要在前面添加 \
进行转义。数组
示例:编程语言
/Hello World/
匹配Hello World
工具
全字匹配的时候,有时咱们要限制匹配结果所在的位置,好比,当你要在一堆手机号中查找 130
号段的号码,此时若是使用全字匹配,你可能会匹配到 18712341301
这样的结果,由于其中包含了 130
这个子串,可是咱们须要的是 130
开头的号码,因此全字匹配不能知足需求了。此时,只要在全字匹配的正则中添加一个特殊符号 ^
,这个特殊符号仅仅做为一个位置标记符,表示整个要匹配的目标字符串的开头,要注意这一点,它是一个表示开始位置的占位符。spa
示例:code
/^130/
仅匹配处于字符串开头位置的130
,好比13012345678
,而相似于11302345678
这种虽然包含130
,可是不是在最开头包含的,则会匹配失败。three
与 ^
匹配开始位置对应的是 $
,表示要匹配的目标字符串的结尾,它也仅仅是一个表示位置的占位符。ip
示例:
/ed$/
仅匹配处于字符串结尾的ed
,好比opened
、closed
,而相似于edge
、bedroom
这些虽然包含ed
,可是不在结尾的,则会匹配失败。
/^Hello World$/
仅匹配既处于开头又处于结尾的Hello World
,所以只有一种状况是知足的:也就是整个匹配的目标字符串就只有Hello World
。
有时,咱们须要的匹配的内容并不直接肯定,可是要求必定属于某个集合内。好比要匹配性别:男
、女
,咱们就不能直接使用全字匹配了。此时,咱们可使用一个特殊符号 |
,表示“或者”。这样咱们就能够将全部须要匹配的元素集合依次列出来,使用 |
分隔便可。要注意的是,匹配结果必定是二选一。
示例:
/男|女/
能够匹配到一个字符串中包含的男
或者女
,好比:性别:男
,而若是目标字符串中即不包含男
也不包含女
,则匹配失败。
/^男|女$/
仅当目标字符串只有一个字“男”或者只有一个字“女”的时候能够匹配成功。而相似于男女
这样两个字,虽然既包含了男
也包含了女
,其中男
处于开头位置,女
处于结尾位置,可是并不存在“既处于开头又处于结尾的”男
或女
,因此是匹配失败的。
/one|two|three|four|five/
匹配 1~5 的英文数。因而可知,你能够听任意多个须要匹配的元素列表,匹配结果将是列表中的任意一个。
有了 |
,咱们能够把想要匹配的子串所有列举出来,但有时,你会发现这是个大工程:好比,咱们想要匹配手机号码,在中国,手机号码长度一般都是 11 位,而且开头三位表示号段,有 130
、131
等(而且还在增长),后面跟着 8 位数字。如今想要匹配这样的数字串,使用上面的方法就不太够了,你也许须要把全部可能的手机号所有列出来?
不过正则里有一个万能字符,使用特殊符号 .
表示(就是小数点),这个符号能够表示任意单个字符。
示例:
/^130........$/
能够匹配130
开头,后面跟着 8 位任意字符的字符串,好比13012345678
、130abcdefgh
。
实际上,上面这个匹配手机号的例子有点粗糙,它虽然能够匹配到全部 130
开头的手机号,但也能够匹配到后面跟的不是数字的字符串,好比 130abcdefgh
,此时,咱们若是想要完完整整的匹配手机号,也就是后面 8 位要求必定是数字,这样的话就不能用 .
了,由于它表示任意字符,而咱们只要 0~9 这十个字符。
正则表达式中存在一些预约义的“字符类”,好比这里咱们只要匹配 0~9 这十个字符,在正则中可使用 \d
表示,它与 .
相似,表示一个字符,不同的是它仅仅表示 0~9 中的任意一个字符,有点相似于 /0|1|2|3|4|5|6|7|8|9/
。
示例:
/^130\d\d\d\d\d\d\d\d$/
能够匹配130
开头,后面跟着 8 位任意数字的字符串,好比13012345678
、13087654321
,而上个示例中的130abcdefgh
将再也不匹配,由于后面 8 位不是数字。
除了 \d
,正则中还有两个比较经常使用的字符类:\w
和 \s
。其中 \w
在大部分正则引擎中表示 26 个大写英文字母 + 26 个小写英文字母 + 0~9 10 个数字 + 下划线字符 _
,一共 63 个字符中的任意一个;而 \s
通常表示“空白字符”,好比 空格、
\t
制表符、\n
换行符、\f
换页符等。
但值得注意的是,上面这三个字符类 \d
、\w
和 \s
具体包含哪些字符是根据不一样的正则引擎实现而不一样的!可是大致上来讲,\d
就是数字,\w
是单词符号、\s
是空白符号。
在一些特定的正则引擎实现中,还会包含一些其余的字符类,具体要根据对应的正则引擎而决定。
有了 \d
、\w
、\s
三个字符类,正则还提供了他们的补集,使用他们的大写来表示,好比 \D
表示全部不是数字的字符、\W
表示全部不是单词符号的字符、\S
表示全部不是空白字符的字符。
虽说正则中提供了三个预约义的字符类,以及他们的补集,有的时候仍是不太够用。好比,咱们要匹配十六进制数,十六进制数除了包含 0~9 十个数字字符外,还包含 a~f (不区分大小写)六个(或者说十二个)英文字母,这样一来简单的 \d
就不够了,\w
又包含多余的字符会致使匹配的结果不许确。
正则提供了自定义字符类的方法,只要将所需的字符用中括号 []
括起来,便可造成一个自定义的字符类。
示例:
/^[0123456789abcdefABCDEF]$/
能够匹配一位十六进制数。
像上面这样把须要的字符列出来就能够造成一个自定义字符类,可是当字符数比较多的时候,这样全列出来就感受有点麻烦了。在自定义字符类中,可使用 -
符号来定义区间,能够直接指定从一个起始字符到一个结束字符。因此上面的例子能够改为这样:
示例:
/^[0-9a-fA-F]$/
能够匹配一位十六进制数。
这样是否是就好多了呢?固然也能够指定更精细的区间,好比 /[0-37-9]/
表示 0、一、二、三、七、八、9 组成的字符类。
与正则预置的字符类同样,自定义字符类一样支持取补集,只要在自定义字符类的第一个位置放置一个 ^
符号(注意,这里的 ^
再也不是字符串开头的含义了),这样就表示不包含后面列出的字符组成的字符类。
示例:
/^[^a-z]$/
匹配不是小写英文字母的单个字符,好比 数字0
、大写字母A
、特殊符号@
等。
在自定义字符类中,保留的特殊符号与第一节中说的那些不太同样了,只有 ]
、-
、^
和 \
有特殊用途,做为特殊符号,须要转义,而其余诸如 $
、|
之类的其余符号在自定义字符类中都再也不须要转义(固然,你想继续转义也是能够的,可是会下降正则的可读性)。
固然,这些特殊符号也能够在他们没有实际用途的时候,不进行转义。这句话有点绕,具体来讲,好比 -
,在自定义字符类中表示区间范围,好比 /[a-c]/
表示 a
、b
和 c
组成的字符类,可是若是你将它放在字符类的开头或者结尾,它将没法构成范围,好比 /[-a]/
或者 /[a-]/
就表示 a
和 -
组成的字符类。再好比 ^
放在自定义字符类的开头表示补集,可是放在其余位置就再也不有特殊意义,就不须要转义了。而 \
做为转义符,自身永远都须要被转义 \\
。
还有 ]
,这个做为一个定界符,表示自定义字符类的定义结束,可是若是将它放在自定义字符类的开头,好比 /[]a]/
表示 ]
和 a
组成的字符类;或是放在第二个位置,而第一个位置是 ^
,好比 /[^]a/
表示不包含 ]
和 a
的字符类。可是注意,这在 JavaScript 中不适用!在 JavaScript 中,/[]/
不管什么时候都表示一个永远没法匹配成功的空字符类,而 /[^]/
表示能够匹配任意单个字符的字符类,因此在 JavaScript 中 ]
不管如何都有特殊意义,所以永远都须要使用 \
进行转义!
大多数在自定义字符类外面的转义标记也能够在自定义字符类中生效,好比不可打印字符(换行 \n
之类的)、八进制转义符、十六进制转义符、Unicode 转义符。
示例:
/^[\^\]\-\\]$/
匹配^
、]
、-
或\
。
在部分正则引擎中(好比 .NET、XPath 等),自定义字符类还支持减法。好比 /[a-z-[aeiou]]/
表示匹配全部小写的辅音英文字母,也就是从 a
到 z
这全部的 26 个英文字母中去除 a
、e
、i
、o
和 u
这五个元音字母。
还有部分正则引擎(好比 Java、Ruby 等),自定义字符类还支持交集。好比 /[a-z&&[^aeiou]]/
也表示匹配全部小写的辅音英文字母,可是原理和上面不同,这个是要求字符既在 a
到 z
这 26 个英文字母之中,又不能在 a
、e
、i
、o
和 u
这五个缘由字母中。
到目前为止,全部的正则表达式都是“静态”的,也就是你写了什么就匹配到什么,顶多使用字符类来代替多种字符。可是看看上面匹配手机号的示例,后面跟着 8 个 \d
,这很是尴尬,若是后面跟着更多的数字怎么办?
正则中提供了“重复”功能,能够将前面的一个匹配内容重复屡次。使用特殊符号 +
或 *
,可使得前面一个匹配单元重复匹配屡次,其中 +
要求至少出现一次。
示例:
/^130\d+$/
能够匹配130
开头,后面至少跟一个数字的字符串,好比1301
、130123456789123456789
之类的,后面能够跟任意多个数字,可是130
则不能匹配。
/^130\d*$/
能够匹配130
开头,后面跟不跟数字均可以,但要跟的话必须是跟数字的字符串,好比130
、1301
、130123456789123456789
,而130a
则没法匹配。
⚠ 注意:重复是仅对前一个最小匹配单元生效的,上面的例子中,\d
是最小的匹配单元,因此是对 \d
重复。对于自定义字符类,也是属于一个最小匹配单元。
+
和 *
的重复次数是上不封顶的,因此在匹配手机号的时候就用不到了,由于手机号后面固定是 8 位数字。此时,可使用更加灵活的重复控制方法:{m,n}
(中间不能有空格),这表示最少重复 m 次,最多重复 n 次(注意 m 和 n 都是闭区间)。
特别的,若是 m 与 n 相等的话,则能够省略为 {m}
,表示固定重复 m 次;而若是 n 等于正无穷的话,能够省略 n 变为 {m,}
,表示最少重复 m 次,上不封顶。
示例:
/^130\d{2,5}$/
能够匹配130
开头,后面跟着至少 2 个,至多 5 个数字的字符串,好比:13012
、130123
、1301234
、13012345
。
/^130\d{2,}$/
能够匹配130
开头,后面跟着至少 2 个数字的字符串,好比:13012
、13012345
、130123456789123456789
。
/^130\d{8}$/
能够匹配130
开头,后面跟着 8 个数字的字符串,好比:13012345678
。
因而可知,+
实际上就是 {1,}
的简写形式,而 *
实际上就是 {0,}
的简写形式。
还有一个特殊的“重复”类型:?
,这个实际上不算是“重复”了,可是属于“重复”的衍生品,它表示不出现,或是出现一次,也就是 {0,1}
的简写形式。
示例:
/^colou?r$/
能够匹配colour
或是color
。
/^https?:\/\/$/
能够匹配http://
或是https://
。这里/
虽然不是特殊符号,可是本文中使用/
来标记正则,因此为了不/
被解析为正则的边界,因此使用\
对其进行了转义。实际上若是你使用的正则引擎不是以/
来标记正则的,那么这里就不须要进行转义,好比:|^https?://$|
。
在上面的重复中,咱们发现,重复只能对前一个最小匹配单元生效,若是咱们想要更灵活的重复怎么办呢?好比圣诞老人来了,HoHoHo~,这里咱们想要匹配这个 HoHoHo
要怎么办呢?
正则中使用小括号 ()
进行分组,括号内能够看做一个独立的子正则表达式个体,整个括号将被做为一个匹配单元对待,所以,咱们只要对一个 Ho
进行分组,而后重复对这个分组进行便可~
示例:
/^(Ho){3}~$/
能够匹配HoHoHo~
。
联系到前面的“或者”,|
会对整个正则表达式生效,若是我有一个相似于这样格式的数据:性别:%s,角色:%s
,其中性别只有 男
、女
两种,角色有 管理员
、游客
两种,如今我想使用正则匹配这个字符串,简单的使用 |
就办不到了,此时就只有将先后两个须要使用到 |
的部分分别进行分组。
示例:
/^性别:(男|女),角色:(管理员|游客)$/
:匹配性别:男,角色:管理员
、性别:男,角色:游客
、性别:女,角色:管理员
或性别:女,角色:游客
。
到如今,咱们基本上能够通吃绝大多数的状况了。在有了分组以后,咱们能够解锁一个新的技能:引用一个分组。
正则的一个很是经常使用的功能就是分组引用。在上面的例子 /^性别:(男|女),角色:(管理员|游客)$/
中咱们能够看到有两个分组,第一个是 (男|女)
,第二个是 (管理员|游客)
,这样在匹配的时候就能够获得两个可使用的分组。最直观的能够体如今各个编程语言的匹配结果中,一般匹配结果会以一个数组形式(或者是类数组)返回,一般数组的第 0 个元素为匹配到的整个字符子串,而从第 1 个元素开始,表示匹配到的第一个分组的内容,第 2 个元素表示匹配到的第二个分组的内容。
所以,字符串 "性别:男,角色:管理员"
使用 /^性别:(男|女),角色:(管理员|游客)$/
匹配获得的结果是:
[
"性别:男,角色:管理员",
"男",
"管理员"
]
复制代码
分组除了能够在匹配结果中使用外,还能够在正则内部使用,好比,我要匹配使用一对引号引发来的字符串,引号能够是英文单引号,也能够是英文双引号。好比 'hello'
、"world"
,若是咱们简单的使用 /^['"]\w+['"]$/
,虽然能够成功匹配这两种状况,可是对于 'hello"
、"world'
这种先后括号不匹配的状况也能够匹配成功,这显然不是咱们想要的。这时,使用分组的引用就能够解决这个问题:
示例:
/^(['"])\w+\1$/
能够匹配由英文单引号或是英文双引号引发来的单词,而且确保先后的引号是匹配的。
这里咱们为 \w
前面的引号进行了一次分组,此时这个分组会被编号为 1,这样在后面咱们使用 \1
引用前面的这个分组,就能够确保先后引号一致了~
这里要注意一下八进制转义符,八进制转义符不太统一,但一般是 \
后直接跟数字,这就与分组引用冲突了,由于分组引用也是 \
后直接跟数字。因此通常建议是不要使用八进制转义符,八进制一般能够很容易转成十六进制,所以建议在使用字符编号表示字符的时候,不要使用八进制,而是使用十六进制!
好比字母 'a'
,编号为十进制为 97,八进制为 141,十六进制为 61。那么八进制表示为 \141
,十六进制表示为 \x61
,或者使用 Unicode 表示为 \u0061
。