十分感谢对小编的支持,这篇文章绝不会让你失望!
引言
正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来:
(1)检查一个串中是否含有符合某个规则的子串,并且可以得到这个子串;
(2)根据匹配规则对字符串进行灵活的替换操作。
目录总览
一、字符
1.普通字符
- 字母
- 数字
- 汉字
- 下划线
- 没有特殊定义的标点符号
表达式中的普通字符在匹配一个字符串时,匹配与之相同的一个字符。
举例 1:表达式 “c”,在匹配字符串 “abcde” 时
- 匹配结果:成功
- 匹配到的内容:“c”;
- 匹配到的位置:开始于 2,结束于 3。
- (注:下标从 0 开始还是从 1 开始,因当前编程语言的不同而可能不同)
举例 2:表达式 “bcd”,在匹配字符串 “abcde” 时
- 匹配结果:成功
- 匹配到的内容:“bcd”;
- 匹配到的位置:开始于 1,结束于 4。
2.转义字符
一些不便书写的字符,采用在前面加\
的方法。
表达式 | 可匹配 |
---|---|
\r | 回车 |
\n | 换行符 |
\t | 制表符 |
\\ | \ 本身 |
3.特殊字符
有特殊用处的标点符号,在前面加 \
后,就代表该符号本身。比如:^, $ 都有特殊意义,如果要想匹配字符串中^
和$
字符,则表达式就需要写成\^
和\$
。
举例 1:表达式
"\$d"
,在匹配字符串 “abc$de” 时,
- 匹配结果是:成功;
- 匹配到的内容是:“$d”;
- 匹配到的位置是:开始于 3,结束于 5。
二、表达式
1.匹配字符
\d
\w
\s
.
正则表达式中的一些表示方法,可以匹配 ‘多种字符’ 其中的任意一个字符。比如,表达式"\d"可以匹配任意一个数字。虽然可以匹配其中任意字符,但是只能是一个,不是多个。这就好比玩扑克牌时候,大小王可以代替任意一张牌,但是只能代替一张牌。
表达式 | 可匹配 |
---|---|
\d | 任意一个数字,0~9 中的任意一个 |
\w | 任意一个字母或数字或下划线,也就是 AZ,az,0~9,_ 中任意一个 |
\s | 包括空格、制表符、换页符等空白字符的其中任意一个 |
. | 小数点可以匹配除了换行符(\n)以外的任意一个字符 |
举例 1:表达式 “\d\d”,在匹配 “abc123” 时,
- 匹配的结果是:成功;
- 匹配到的内容是:“12”;
- 匹配到的位置是:开始于 3,结束于 5。
举例 2:表达式 “a.\d”,在匹配 “aaa100” 时,
- 匹配的结果是:成功;
- 匹配到的内容是:“aa1”;
- 匹配到的位置是:开始于 1,结束于 4。
2.自定义匹配字符
使用方括号 [ ] 包含一系列字符,能够匹配其中任意一个字符。用 [^ ]
包含一系列字符,则能够匹配其中字符之外的任意一个字符。同样的道理,虽然可以匹配其中任意一个,但是只能是一个,不是多个。
表达式 | 可匹配 |
---|---|
[ab5@] | 匹配 “a” 或 “b” 或 “5” 或 “@” |
[^abc] | 匹配 “a”,“b”,“c” 之外的任意一个字符 |
[f-k] | 匹配 “f”~“k” 之间的任意一个字母 |
[^A-F0-3] | 匹配 “A”“F”,"0"“3” 之外的任意一个字符 |
举例 1:表达式
"[bcd][bcd]"
匹配 “abc123” 时,
- 匹配的结果是:成功;
- 匹配到的内容是:“bc”;
- 匹配到的位置是:开始于 1,结束于 3。
举例 2:表达式
"[^abc]"
匹配 “abc123” 时,
- 匹配的结果是:成功;
- 匹配到的内容是:“1”;
- 匹配到的位置是:开始于 3,结束于 4。
三、特殊符号
1.修饰匹配次数
{n}
{m,n}
{m,}
?
+
*
使用方法是:"次数修饰"放在"被修饰的表达式"后边。比如:"[bcd][bcd]"
可以写成"[bcd]{2}"
。
表达式 | 作用 |
---|---|
{n} | 表达式重复 n 次,比如:“\w{2}” 相当于 “\w\w”;“a{5}” 相当于 “aaaaa” |
{m,n} | 表达式至少重复 m 次,最多重复 n 次,比如:"ba{1,3}"可以匹配 “ba"或"baa"或"baaa” |
{m,} | 表达式至少重复 m 次,比如:“\w\d{2,}“可以匹配 “a12”,”_456”,“M12344”… |
? | 匹配表达式 0 次或者 1 次,相当于 {0,1},比如:"a[cd]?"可以匹配 “a”,“ac”,“ad” |
+ | 表达式至少出现 1 次,相当于 {1,},比如:"a+b"可以匹配 “ab”,“aab”,“aaab”… |
* | 表达式不出现或出现任意次,相当于 {0,},比如:“^*b"可以匹配 “b”,”^^^b"… |
2.代表抽象意义
^
$
\b
表达式 | 作用 |
---|---|
^ | 与字符串开始的地方匹配,不匹配任何字符 |
$ | 与字符串结束的地方匹配,不匹配任何字符 |
\b | 匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符 |
举例 1:表达式 “^aaa” 在匹配 “xxx aaa xxx” 时,
- 匹配结果是:失败。
- 因为 “^” 要求与字符串开始的地方匹配,因此,只有当 “aaa” 位于字符串的开头的时候,“^aaa” 才能匹配,比如:“aaa xxx xxx”。
举例 2:表达式 “aaa$” 在匹配 “xxx aaa xxx” 时,
- 匹配结果是:失败。
- 因为 “ " 要求与字符串结束的地方匹配,因此,只有当 " a a a " 位于字符串的结尾的时候, " a a a " 要求与字符串结束的地方匹配,因此,只有当 "aaa" 位于字符串的结尾的时候,"aaa "要求与字符串结束的地方匹配,因此,只有当"aaa"位于字符串的结尾的时候,"aaa” 才能匹配,比如:“xxx xxx aaa”。
举例 3:表达式 “.\b.” 在匹配 “@@@abc” 时,
- 匹配结果是:成功;
- 匹配到的内容是:“@a”;
- 匹配到的位置是:开始于 2,结束于 4。
- 进一步说明:“\b” 与 “^” 和 “$” 类似,本身不匹配任何字符,但是它要求它在匹配结果中所处位置的左右两边,其中一边是 “\w” 范围,另一边是 非"\w" 的范围。
举例 4:表达式 “\bend\b” 在匹配 “weekend,endfor,end” 时,
- 匹配结果是:成功;匹
- 配到的内容是:“end”;
- 匹配到的位置是:开始于 15,结束于 18。
3.影响子表达式间关系
|
( )
表达式 | 作用 |
---|---|
| | 左右两边表达式之间 “或” 关系,匹配左边或者右边 |
( ) | 1. 在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰 2. 取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到 3. 括号 “( )” 内的子表达式,如果希望匹配结果不进行记录供以后使用,可以使用"(?:xxxxx)" 格式 |
举例 5:表达式 “Tom|Jack” 在匹配字符串 “I’m Tom, he is Jack” 时,
- 匹配结果是:成功;
- 匹配到的内容是:“Tom”;
- 匹配到的位置是:开始于 4,结束于 7。
- 匹配下一个时,
- 匹配结果是:成功;
- 匹配到的内容是:“Jack”;
- 匹配到的位置时:开始于 15,结束于 19。
举例 6:表达式 “(go\s*)+” 在匹配 “Let’s go go go!” 时,
- 匹配结果是:成功;
- 匹配到内容是:“go go go”;
- 匹配到的位置是:开始于 6,结束于 14。
举例 7:表达式 “¥(\d+.?\d*)” 在匹配 “$10.9,¥20.5” 时,
- 匹配的结果是:成功;
- 匹配到的内容是:“¥20.5”;
- 匹配到的位置是:开始于 6,结束于 10。单独获取括号范围匹配到的内容是:“20.5”。
举例 1:表达式 “(?😦\w)\1)+” 匹配 “a bbccdd efg” 时,
- 结果是 “bbccdd”。
- 括号 “(?😃” 范围的匹配结果不进行记录,因此 “(\w)” 使用 “\1” 来引用。
四、高级规则
1.非贪婪模式
在使用修饰匹配次数的特殊符号时,有几种表示方法可以使同一个表达式能够匹配不同的次数,比如:“{m,n}”, “{m,}”, “?”, “*”, “+”,具体匹配的次数随被匹配的字符串而定。这种重复匹配不定次数的表达式在匹配过程中,总是尽可能多的匹配。这种匹配原则就叫作 “贪婪” 模式 。
在修饰匹配次数的特殊符号后再加上一个 “?” 号,则可以使匹配次数不定的表达式尽可能少的匹配,使可匹配可不匹配的表达式,尽可能的 “不匹配”。这种匹配原则叫作 “非贪婪” 模式,也叫作 “勉强” 模式。如果少匹配就会导致整个表达式匹配失败的时候,与贪婪模式类似,非贪婪模式会最小限度的再匹配一些,以使整个表达式匹配成功。
表达式 | 匹配结果 |
---|---|
(d)(\w+) | “\w+” 将匹配第一个 “d” 之后的所有字符 “xxxdxxxd” |
(d)(\w+?) | “\w+?” 将尽可能少的匹配第一个 “d” 之后的字符,结果是:“\w+?” 只匹配了一个 “x” |
(d)(\w+)(d) | “\w+” 将匹配第一个 “d” 和最后一个 “d” 之间的所有字符 “xxxdxxx”。虽然 “\w+” 也能够匹配上最后一个 “d”,但是为了使整个表达式匹配成功,“\w+” 可以 “让出” 它本来能够匹配的最后一个 “d” |
(d)(\w+?)(d) | 为了让整个表达式匹配成功,“\w+?” 不得不匹配 “xxx” 才可以让后边的 “d” 匹配,从而使整个表达式匹配成功。因此,结果是:“\w+?” 匹配 “xxx” |
举例 1:表达式 “(.*)” 与字符串 “
aa
bb
” 匹配时
- 匹配的结果是:成功;
- 匹配到的内容是"
aa
bb
" 整个字符串,- 表达式中的 “” 将与字符串中最后一个 “” 匹配。
举例 2:相比之下,表达式 “(.*?)” 匹配举例 1 中同样的字符串时,将只得到"
aa
“, 再次匹配下一个时,可以得到第二个”bb
"。
2.()反向引用
“小括号包含的表达式所匹配到的字符串” 不仅是在匹配结束后才可以使用,在匹配过程中也可以使用。表达式后边的部分,可引用前面 “括号内的子匹配已经匹配到的字符串”。引用方法是 "\"
加上一个数字。“\1” 引用第 1 对括号内匹配到的字符串,“\2” 引用第 2 对括号内匹配到的字符串……以此类推,如果一对括号内包含另一对括号,则外层的括号先排序号。换句话说,哪一对的左括号 “(” 在前,那这一对就先排序号。
举例 1:表达式 “('|”)(.*?)(\1)" 在匹配 " ‘Hello’, “World” " 时
- 匹配结果是:成功;
- 匹配到的内容是:" ‘Hello’ "。
- 再次匹配下一个时,可以匹配到 " “World” "。
举例 2:表达式 “(\w)\1{4,}” 在匹配 “aa bbbb abcdefg ccccc 111121111 999999999” 时
- 匹配结果是:成功;
- 匹配到的内容是 “ccccc”。
- 再次匹配下一个时,将得到999999999。
- 这个表达式要求 “\w” 范围的字符至少重复 5 次,注意与 “\w{5,}” 之间的区别。
举例 3:表达式 “<(\w+)\s*(\w+(=('|”).?\4)?\s)>.?</\1>" 在匹配 “” 时
- 匹配结果是成功。
- 如果 “” 与 “” 不配对,则会匹配失败;
- 如果改成其他配对,也可以匹配成功。
3.预搜索
1)正向预搜索
正向预搜索:“(?=xxxxx)”,“(?!xxxxx)”
零宽度正预测先行断言
零宽度正预测先行断言:(?=xxxxx)
,匹配的是当前位置之后的文本,但不把它包含在结果中。对所处的 “缝隙” 或 “两头” 附加条件:所在缝隙右侧,必须能够匹配上 xxxxx 表达式。不影响后边表达式匹配缝隙后的字符。这就类似 “\b”,本身不匹配任何字符。“\b” 只是将所在缝隙之前、之后的字符取来进行判断,不会影响后边表达式的匹配。
举例 1:表达式 “Windows (?=NT|XP)” 在匹配 “Windows 98, Windows NT,Windows 2000” 时,将只匹配 “Windows NT” 中的 "Windows ",其他的 "Windows " 字样则不被匹配。
举例 2:表达式 “(\w)((?=\1\1\1)(\1))+” 在匹配字符串 “aaa ffffff 999999999” 时,将可以匹配 6 个"f"的前 4 个,可以匹配 9 个"9"的前 7 个。这个表达式可以读解成:重复 4 次以上的字母数字,则匹配其剩下最后 2 位之前的部分。当然,这个表达式可以不这样写,在此的目的是作为演示之用。
零宽度负预测先行断言
零宽度负预测先行断言:(?!xxxxx)
,它匹配的是当前位置之后的文本,但需要满足后面的模式 xxxxx
条件不成立。需要注意的是,负预测中使用到的模式可以是任何正则表达式,不一定只有单个字符或数字等。
所在缝隙的右侧,必须不能匹配 xxxxx 这部分表达式。
举例 3:表达式 “((?!\bstop\b).)+” 在匹配 “fdjka ljfdl stop fjdsla fdj” 时,将从头一直匹配到 “stop” 之前的位置,如果字符串中没有 “stop”,则匹配整个字符串。
举例 4:表达式 “do(?!\w)” 在匹配字符串 “done, do, dog” 时,只能匹配 “do”。在本条举例中,“do” 后边使用 “(?!\w)” 和使用 “\b” 效果是一样的。
2)负向预搜索
和正向预搜索是类似的,反向预搜索要求的条件是:所在"缝隙" 或 “两头"的 “左侧”,两种格式分别要求必须能够匹配和必须不能够匹配指定表达式,而不是去判断右侧。与 “正向预搜索” 一样的是:它们都是对所在"缝隙” 或 "两头"的一种附加条件,本身都不匹配任何字符。
零宽度正回顾后发断言
零宽度正回顾后发断言:(?<=xxxxx)
,匹配的是当前位置之前的文本,但不把它包含在结果中。
零宽度负回顾后发断言
零宽度负回顾后发断言:(?<!xxxxx)
,它匹配的是当前位置之前的文本,但需要满足后面的模式 xxxxx
条件不成立。
举例 5:表达式 “(?<=\d{4})\d+(?=\d{4})” 在匹配 “1234567890123456” 时,将匹配除了前 4 个数字和后 4 个数字之外的中间 8 个数字。
表达式 “(?<=\d{4})\d+(?=\d{4})” 中,先匹配了前4个数字 \d{4},然后使用了零宽度正回顾后发断言 (?<=\d{4}),表示要求前面必须有4个数字。接着匹配了中间的数字 \d+,最后使用了零宽度正预测先行断言 (?=\d{4}),表示要求后面必须有4个数字。因此只会匹配中间8个数字。
举例 6:如果将表达式改为
(?=\d{4})\d+(?<=\d{4})
,则在匹配 “1234567890123456” 这个字符串时,会返回整个字符串,而不是把中间的 8 个数字取出来。这是因为这个表达式的含义是:
- 匹配当前位置之后的四个数字:
(?=\d{4})
;- 匹配一或多个数字:
\d+
;- 匹配当前位置之前的四个数字:
(?<=\d{4})
。在 “1234567890123456” 这个字符串中,第一个匹配项是位置 0,匹配到整个字符串 “1234567890123456”,因为它的前后都有四个连续数字。
表达式 “(?=\d{4})\d+(?<=\d{4})” 中,则是先使用了零宽度正预测先行断言 (?=\d{4}),表示要求后面必须有4个数字。然后才开始匹配中间的数字 \d+。最后使用了零宽度正回顾后发断言 (?<=\d{4}),表示要求前面必须有4个数字。由于整个字符串都满足这些条件(前面和后面都有四位数字),所以会把整个字符串都匹配上。
正则表达式是从左到右进行匹配的,也就是说,先匹配左边的内容,然后再匹配右边的内容。在这两个表达式中,它们的顺序是不同的。简单来说,在第一个表达式中,我们先确定了中间需要匹配的内容,然后再确定前后的限制条件。而在第二个表达式中,则是先确定了前后的限制条件,然后再去匹配中间的内容。
很多其他的引擎可以支持反向预搜索,比如:Java 1.4 以上的 java.util.regex 包,.NET 中 System.Text.RegularExpressions 命名空间,boost::regex 以及 GRETA 正则表达式库等。
五、其他通用规则
1.表示字符
表达式中,可以使用 “\xXX” 和 “\uXXXX” 表示一个字符(“X” 表示一个十六进制数)
形式 | 字符范围 |
---|---|
\xXX | 编号在 0 ~ 255 范围的字符,比如:空格可以使用 “\x20” 表示 |
\uXXXX | 任何字符可以使用 “\u” 再加上其编号的 4 位十六进制数表示,比如:“\u4E2D” |
2.大小写规则
表达式 | 可匹配 |
---|---|
\S | 匹配所有非空白字符(“\s” 可匹配各个空白字符) |
\D | 匹配所有的非数字字符 |
\W | 匹配所有的字母、数字、下划线以外的字符 |
\B | 匹配非单词边界,即左右两边都是 “\w” 范围或者左右两边都不是 “\w” 范围时的字符缝隙 |
3.加\匹配字符
字符 | 说明 |
---|---|
^ | 匹配输入字符串的开始位置。要匹配"^" 字符本身,请使用"\^" |
$ | 匹配输入字符串的结尾位置。要匹配 “$” 字符本身,请使用"\$" |
() | 标记一个子表达式的开始和结束位置。要匹配小括号,请使用"\("和"\)" |
[] | 用来自定义能够匹配 ‘多种字符’ 的表达式。要匹配中括号,请使用"\["和"\]" |
{} | 修饰匹配次数的符号。要匹配大括号,请使用"\{"和"\}" |
. | 匹配除了换行符(\n)以外的任意一个字符。要匹配小数点本身,请使用"\." |
? | 修饰匹配次数为0次或1次。要匹配 “?” 字符本身,请使用"?" |
+ | 修饰匹配次数为至少1次。要匹配 “+” 字符本身,请使用"\+" |
* | 修饰匹配次数为0次或任意次。要匹配 “*” 字符本身,请使用"\*" |
| | 左右两边表达式之间"或"关系。匹配 “|” 本身,请使用"|" |
4.表达式属性
表达式属性 | 说明 |
---|---|
Ignorecase | 默认情况下,表达式中的字母是要区分大小写的。配置为Ignorecase可使匹配 时不区分大小写。有的表达式引擎,把"大小写"概念延伸至UNICODE范围的 大小写。 |
Singleline | 默认情况下,小数点"."匹配除了换行符(\n)以外的字符。配置为Singleline 可使小数点可匹配包括换行符在内的所有字符。 |
Multiline | 默认情况下,表达式"^“和”KaTeX parse error: Undefined control sequence: \n at position 36: … ①xxxxxxxxx②\̲n̲ ̲ ̲<br />③xxxxxxxx…"还可以匹配换行符之前, 一行结束②的位置。 |
Global | 主要在将表达式用来替换时起作用,配置为Global表示替换所有的匹配。 |
六、综合提示
- 如果要要求表达式所匹配的内容是整个字符串,而不是从字符串中找一部分,那么可以在表达式的首尾使用 “^” 和 “KaTeX parse error: Undefined control sequence: \d at position 8: ",比如:"^\̲d̲+” 要求整个字符串只有数字。
- 如果要求匹配的内容是一个完整的单词,而不会是单词的一部分,那么在表达式首尾使用"\b",比如:使用 “\b(if|while|else|void|int……)\b” 来匹配程序中的关键字。
- 表达式不要匹配空字符串。否则会一直得到匹配成功,而结果什么都没有匹配到。比如:准备写一个匹配 “123”、“123.”、“123.5”、“.5” 这几种形式的表达式时,整数、小数点、小数数字都可以省略,但是不要将表达式写成:“\d*.?\d*”,因为如果什么都没有,这个表达式也可以匹配成功。更好的写法是:“\d+.?\d*|.\d+”。
- 能匹配空字符串的子匹配不要循环无限次。如果括号内的子表达式中的每一部分都可以匹配 0 次,而这个括号整体又可以匹配无限次,那么情况可能比上一条所说的更严重,匹配过程中可能死循环。虽然现在有些正则表达式引擎已经通过办法避免了这种情况出现死循环了,比如 .NET 的正则表达式,但是我们仍然应该尽量避免出现这种情况。如果我们在写表达式时遇到了死循环,也可以从这一点入手,查找一下是否是本条所说的原因。
- 合理选择贪婪模式与非贪婪模式。
- 或 “|” 的左右两边,对某个字符最好只有一边可以匹配,这样,不会因为 “|” 两边的表达式因为交换位置而有所不同。
看到最后啦,休息一下吧。临走之前,小编希望你在未来学习无阻,工作顺利!加油!!!