正则表达式学习记录(一)——正则表达式的编写

文章目录

正则表达式的编写

交互式学习正则表达式:RegexOne 中文

正则表达式练习:RegExr

正则表达式文档:MDN Web Docs 正则表达式

基础篇

标记

说明
样例
直接匹配,输入什么匹配什么。 RegEx: abc
Match: abc
Match: xyzabcdef
. 匹配任何单个字符。 RegEx: .a
Match: 123abc
\ 转义字符,用于匹配某个用于标记的原始字符。 RegEx: \.
Match: Hi.
Match: 3.14
[] 匹配特定范围内的单个字符 RegEx: [abc]an
Match: banana
Match: abcan
[-] 用简略的方式表示范围。例如 [2-6] 等价于 [23456] RegEx: [0-3][a-c]
Match: 1a2b8y9z
[^] 排除特定范围内的单个字符 RegEx: b[^e]r
Match: barber
\d 匹配 0-9 中的单个数字字符,相当于 [0-9] RegEx: \da
Match: 01a2b34
\D 匹配单个非数字字符,相当于 [^0-9] RegEx: \D2
Match: 1aa2b34
\s 匹配单个空白字符,相当于 [\f\n\r\t\v\u0020\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]

其中,\f\n\r\t\v 与 ASCII 字符集中相同记号的转义字符同义,\uhhhh 为 Unicode 字符集对应字符的编号。
RegEx: \s
Match: Hello, world!
\S 匹配单个非空白字符,相当于 [^\f\n\r\t\v\u0020\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] RegEx: \S
Match: Hello,world!
\w 匹配单个单字字符,等价于 [A-Za-z0-9_] RegEx: \w
Match: 3.14
\W 匹配单个非单字字符,等价于 [^A-Za-z0-9_] RegEx: \W
Match: 3.14
\b 表示单词边界,匹配一个单词的开始或结束,而不匹配任何实际字符 RegEx: \bword\b
Match: word and sword
\B 表示单词边界,匹配字母或数字中间的位置,而不匹配任何实际字符 RegEx: \d\B\w\B
Match: 01AB
Match: A4A2-g74t
+ 用于匹配 1 个或更多前面的标记。 RegEx: a+b+
Match: aaabb
* 用于匹配任意多个前面的标记,匹配的前面标记的数量可以为 0 RegEx: a+b*c
Match: aaabbbccc
Match: aaaccc
? 用于匹配前一个标记出现 0 次或 1 次 RegEx: apples?
Match: apple
Match: appless
{} 用于匹配指定数量的前一个标记。 RegEx: \d{2}
Match: Jan 14, 2005

{,}

用于匹配指定数量范围的前一个标记,, 后可以为空。当 , 后留空时,则标识匹配前一个标记的最小值。 RegEx: \d{3,}
Match: Jan 14, 2005
^ 放在标记前,用于匹配开头的字符。 RegEx: ^[Oo]n[Ee]
Match: One by onE
$ 放在标记后,用于匹配末尾的字符。 RegEx: [Oo]n[Ee]$
Match: One by onE
| 或,| 前后的条件满足一个即可匹配

RegEx: I love (dogs|cats)\.

Match: I love dogs.
Match: I love cats.

进阶篇

  • 捕获组

捕获组用 () 表示,括号中的内容为一组。在一个正则表达式中的捕获组,按照上括号 ( 的顺序进行编号,对于嵌套捕获组同样适用。例如在表达式 ((\d+) plus )\d+ 中,捕获组 ((\d+) plus ) 的编号为 1,而 \d+ 这一捕获组的编号为 2。

在正则表达式中,\ 后直接加一个数字 n 相当于“复制”了第 n 个捕获组,从而允许表达式匹配相同的内容。例如,对于表达式 (\w{3}) plus \1 就可以匹配 "one plus one","two plus two",但是无法匹配 "one plus two"。

如果不想让某个捕获组获得编号,可以在 ( 后加入 ?:,这样捕获组就会成为非捕获组,非捕获组不会获得编号。例如对于 "one plus one, two plus two" 这个字符串中除去 , 之外的内容,可以使用 ((\w{3}) plus \2) 匹配,也可以使用 (?:(\w{3}) plus \1) 匹配。

  • 零宽断言

零宽断言有四种,分别是零宽正向先行断言 (?=)(指定后缀)、零宽正向后行断言 (?<=)(指定前缀)、零宽负向先行断言 (?!)(指定后缀不是)、零宽负向后行断言 (?<!)(指定前缀不是)。其中,被指定为或不为前缀或后缀的内容放在零宽断言中下括号 ) 前,先行断言置于标记后,后行断言置于标记前,且零宽断言只用于限定而不参与匹配。

例如我们想匹配 "hopefully seriously" 这两个单词中 "ly" 前的部分,即 "hopeful" 和 "serious",就可以使用零宽正向先行断言匹配后缀为 "ly" 的内容,因此我们使用表达式 \w+(?=ly) 进行匹配,并且 "ly" 并不会被匹配。如果我们想匹配 "Qty.: 100, Price: £150" 中数量 "Qty." 对应的值,则可使用 (?<!£)\d{3}

  • 懒惰匹配

正则表达式默认遵循“贪婪匹配”原则,即尽可能匹配多的字符。例如使用表达式 a\w+c 匹配字符串 "abcabc" 时,会匹配整个字符串,而非单独某一段 "abc"。如果想让匹配尽量短,则可以使用 “懒惰匹配” 模式。懒惰匹配有如下几种模式:+?(出现至少一次,但是长度尽量短)、*?(出现任意次,但是长度尽量短)、??(至多出现一次,但是长度尽量短)、{,}?(出现指定次数,但是长度尽量短)。

例如对于字符串 "abcabc",若需要其匹配为两段 "abc",则可以使用表达式 a\w+?c。使用表达式 a\w{2,7}?c 可以匹配到 "abbbbcabbcabc" 的 "abbbc" 和 "abbc",而非将 "abbbcabbc" 作为一个整体匹配。

另外需要注意的是,正则表达式从字符串的开头开始匹配,使用懒惰匹配只会改变匹配到的结尾的位置,而非开头的位置。例如使用 a\w*?c 匹配字符串 "aaaccc" 会得到 "aaac" 而非 "ac"。

  • 标志位

到目前为止,本文中出现的所有正则表达式都未使用标志位,而前文中所有的表达式都是默认使用 /g 作为标志位得到的相应的结果。正则表达式中有如下常见标志位:g(global,全局匹配)、i(case intensitive,不区分大小写)、m(multiline,多行匹配)、s(single line,单行匹配)。

对于上面的正则表达式 a\w*?c,其完整的写法是 /a\w*?c/g。例如在匹配 "jack_gdn JackGDN JGDN" 这几个单词时,一个完整的正则表达式是 /j.*?gdn/gis 标志位会将多行内容看作一行内容,并且去除 \n\r\nm 标志位对于 ^$ 标记格外有用。例如对于一段文本:

1jack_gdn
2JackGDN
3JGDN

如果使用 /^j.*?gdn/gi 匹配,则只会匹配到第一行的 "jack_gdn",而如果使用 /^j.*?gdn/gim 匹配,则会将三行内容全部匹配上。

测试正则表达式


匹配

匹配结果: