Fork me on GitHub

JavaScript 正则表达式

七言秋雨似情长,宫影空明人不往。
智绝情深不由伤,音泣相思泪以裳。

中二病也要谈恋爱

什么是正则表达式

正则表达式是用于匹配字符串中字符组合的模式。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,简而言之,正则表达式就是对字符串执行模式匹配的强大工具。在 JavaScript 中,正则表达式也是对象。这些模式被用于 RegExp 的 exec 和 test 方法, 以及 String 的 match、replace、search 和 split 方法。

如何创建正则表达式

你可以使用以下两种方法之一构建一个正则表达式:

第一种,使用一个正则表达式字面量,其由包含在斜杠之间的模式组成,如下所示:

1
2
3
4
5
6
7
/*
/pattern/flags
*/

const regex = /ab+c/;

const regex = /^[a-zA-Z]+[0-9]*\W?_$/gi;

在加载脚本后,正则表达式字面值提供正则表达式的编译。当正则表达式保持不变时,使用此方法可获得更好的性能。

第二种,调用RegExp对象的构造函数,如下所示:

1
2
3
4
5
6
7
8
9
/* 
new RegExp(pattern [, flags])
*/

let regex = new RegExp("ab+c");

let regex = new RegExp(/^[a-zA-Z]+[0-9]*\W?_$/, "gi");

let regex = new RegExp("^[a-zA-Z]+[0-9]*\\W?_$", "gi");

使用构造函数提供正则表达式的运行时编译。使用构造函数,当你知道正则表达式模式将会改变,或者你不知道模式,并从另一个来源,如用户输入。

参数说明:

  • 参数 pattern 是一个字符串,指定了正则表达式的模式或其他正则表达式。
  • 参数 attributes 是一个可选的字符串,包含属性 “g”、”i” 和 “m”,分别用于指定全局匹配、区分大小写的匹配和多行匹配。ECMAScript 标准化之前,不支持 m 属性。如果 pattern 是正则表达式,而不是字符串,则必须省略该参数。

PS: 单看概念容易晕,最好结合具体的例子。

特殊字符(规则)

字符类别(Character Classes)

字符 含义
. (点号,小数点) 匹配任意单个字符,但是行结束符除外:\n \r \u2028 或 \u2029。在字符集中,点( . )失去其特殊含义,并匹配一个字面点( . )。需要注意的是,m 多行(multiline)标志不会改变点号的表现。因此为了匹配多行中的字符集,可使用[^] (当然你不是打算用在旧版本 IE 中),它将会匹配任意字符,包括换行符。
例如,/.y/ 匹配 “yes make my day” 中的 “my” 和 “ay”,但是不匹配 “yes”。
\d 匹配任意阿拉伯数字。等价于[0-9]。
例如,/\d/ 或 /[0-9]/ 匹配 “B2 is the suite number.” 中的 ‘2’。
\D 匹配任意一个不是阿拉伯数字的字符。等价于[^0-9]。
例如,/\D/ 或 /[^0-9]/ 匹配 “B2 is the suite number.” 中的 ‘B’。
\w 匹配任意来自基本拉丁字母表中的字母数字字符,还包括下划线。等价于 [A-Za-z0-9_]。
例如,/\w/ 匹配 “apple” 中的 ‘a’,”$5.28” 中的 ‘5’ 和 “3D” 中的 ‘3’。
\W 匹配任意不是基本拉丁字母表中单词(字母数字下划线)字符的字符。等价于 [^A-Za-z0-9_]。
例如,/\W/ 或 /[^A-Za-z0-9_]/ 匹配 “50%” 中的 ‘%’。
\s 匹配一个空白符,包括空格、制表符、换页符、换行符和其他 Unicode 空格。等价于 [ \f\n\r\t\v​\u00a0\u1680​\u180e\u2000​\u2001\u2002​\u2003\u2004​ \u2005\u2006​\u2007\u2008​\u2009\u200a​\u2028\u2029​​\u202f\u205f​ \u3000]。
例如 /\s\w*/ 匹配 “foo bar” 中的 ‘ bar’。
\S 匹配一个非空白符。等价于 [^ \f\n\r\t\v​\u00a0\u1680​\u180e\u2000​\u2001\u2002​\u2003\u2004​ \u2005\u2006​\u2007\u2008​\u2009\u200a​\u2028\u2029​\u202f\u205f​\u3000]。
例如,/\S\w*/ 匹配 “foo bar” 中的 ‘foo’。
\t 匹配一个水平制表符(tab)
\r 匹配一个回车符(carriage return)
\n 匹配一个换行符(linefeed)
\v 匹配一个垂直制表符(vertical tab)
\f 匹配一个换页符(form-feed)
[\b] 匹配一个退格符(backspace)(不要与 \b 混淆)
\0 匹配一个 NUL 字符。不要在此后面跟小数点。
\cX X 是 A - Z 的一个字母。匹配字符串中的一个控制字符。
例如,/\cM/ 匹配字符串中的 control-M。
\xhh 匹配编码为 hh (两个十六进制数字)的字符。
\uhhhh 匹配 Unicode 值为 hhhh (四个十六进制数字)的字符。
\ 对于那些通常被认为字面意义的字符来说,表示下一个字符具有特殊用处,并且不会被按照字面意义解释。
例如 /b/ 匹配字符 ‘b’。在 b 前面加上一个反斜杠,即使用 /\b/,则该字符变得特殊,以为这匹配一个单词边界。
或对于那些通常特殊对待的字符,表示下一个字符不具有特殊用途,会被按照字面意义解释。
例如,* 是一个特殊字符,表示匹配某个字符 0 或多次,如 /a*/ 意味着 0 或多个 “a”。 为了匹配字面意义上的 * ,在它前面加上一个反斜杠,例如,/a\*/匹配 ‘a*‘。

字符集合(Character Sets)

字符 含义
[xyz] 一个字符集合,也叫字符组。匹配集合中的任意一个字符。你可以使用连字符’-‘指定一个范围。
例如,[abcd] 等价于 [a-d],匹配”brisket”中的’b’和”chop”中的’c’。
[^xyz] 一个反义或补充字符集,也叫反义字符组。也就是说,它匹配任意不在括号内的字符。你也可以通过使用连字符 ‘-‘ 指定一个范围内的字符。
例如,[^abc] 等价于 [^a-c]。 第一个匹配的是 “bacon” 中的’o’ 和 “chop” 中的 ‘h’。

边界(Boundaries)

字符 含义
^ 匹配输入开始。如果多行(multiline)标志被设为 true,该字符也会匹配一个断行(line break)符后的开始处。
例如,/^A/ 不匹配 “an A” 中的 “A”,但匹配 “An A” 中的 “A”。
$ 匹配输入结尾。如果多行(multiline)标志被设为 true,该字符也会匹配一个断行(line break)符的前的结尾处。
例如,/t$/ 不匹配 “eater” 中的 “t”,但匹配 “eat” 中的 “t”。
\b 匹配一个零宽单词边界(zero-width word boundary),如一个字母与一个空格之间。 (不要和 [\b] 混淆)
例如,/\bno/ 匹配 “at noon” 中的 “no”,/ly\b/ 匹配 “possibly yesterday.” 中的 “ly”。
\B 匹配一个零宽非单词边界(zero-width non-word boundary),如两个字母之间或两个空格之间。
例如,/\Bon/ 匹配 “at noon” 中的 “on”,/ye\B/ 匹配 “possibly yesterday.” 中的 “y

分组(Grouping)与反向引用(back references)

字符 含义
(x) 匹配 x 并且捕获匹配项。 这被称为捕获括号(capturing parentheses)。
例如,/(foo)/ 匹配且捕获 “foo bar.” 中的 “foo”。被匹配的子字符串可以在结果数组的元素 [1], …, [n] 中找到,或在被定义的 RegExp 对象的属性 $1, …, $9 中找到。捕获组(Capturing groups)有性能惩罚。如果不需再次访问被匹配的子字符串,最好使用非捕获括号(non-capturing parentheses)
\n n 是一个正整数。一个反向引用(back reference),指向正则表达式中第 n 个括号(从左开始数)中匹配的子字符串。
例如,/apple(,)\sorange\1/ 匹配 “apple, orange, cherry, peach.” 中的 “apple,orange,”。
(?:x) 匹配 x 不会捕获匹配项。这被称为非捕获括号(non-capturing parentheses)。匹配项不能够从结果数组的元素 [1], …, [n] 或已被定义的 RegExp 对象的属性 $1, …, $9 再次访问到。

数量词(Quantifiers)

字符 含义
x* 匹配前面的模式 x 0或多次。
例如,/bo*/ 匹配 “A ghost booooed” 中的 “boooo”,”A bird warbled” 中的 “b”,但是不匹配 “A goat grunted”。
x+ 匹配前面的模式 x 1或多次。等价于 {1,}。
例如,/a+/ 匹配 “candy” 中的 “a”,”caaaaaaandy” 中所有的 “a”。
x*?、x+? 像上面的 * 和 + 一样匹配前面的模式 x,然而匹配是最小可能匹配(即非贪婪模式,不加?就是贪婪模式)
例如,/“.*?”/ 匹配 ‘“foo” “bar”‘ 中的 ‘“foo”‘,而 * 后面没有 ? 时匹配 ‘“foo” “bar”‘。
x? 匹配前面的模式 x 0或1次。
例如,/e?le?/ 匹配 “angel” 中的 “el”,”angle” 中的 “le”。如果在数量词 *、+、? 或 {}, 任意一个后面紧跟该符号(?),会使数量词变为非贪婪( non-greedy) ,即匹配次数最小化。反之,默认情况下,是贪婪的(greedy),即匹配次数最大化。
x? 匹配前面的模式 x 0或1次。
例如,/e?le?/ 匹配 “angel” 中的 “el”,”angle” 中的 “le”。如果在数量词 *、+、? 或 {}, 任意一个后面紧跟该符号(?),会使数量词变为非贪婪( non-greedy) ,即匹配次数最小化。反之,默认情况下,是贪婪的(greedy),即匹配次数最大化。
x(?=y)、x(?!y) 这两个放在断言(Assertions)里面讲
x|y 匹配 x 或 y
例如,/green|red/ 匹配 “green apple” 中的 ‘green’,”red apple.” 中的 ‘red’。
x{n} n 是一个正整数。前面的模式 x 连续出现 n 次时匹配。
例如,/a{2}/ 不匹配 “candy,” 中的 “a”,但是匹配 “caandy,” 中的两个 “a”,且匹配 “caaandy.” 中的前两个 “a”。
x{n,} n 是一个正整数。前面的模式 x 连续出现至少 n 次时匹配。
例如,/a{2,}/ 不匹配 “candy” 中的 “a”,但是匹配 “caandy” 和 “caaaaaaandy.” 中所有的 “a”。
x{n,m} n 和 m 为正整数。前面的模式 x 连续出现至少 n 次,至多 m 次时匹配。
例如,/a{1,3}/ 不匹配 “cndy”,匹配 “candy,” 中的 “a”,”caandy,” 中的两个 “a”,匹配 “caaaaaaandy” 中的前面三个 “a”。注意,当匹配 “caaaaaaandy” 时,即使原始字符串拥有更多的 “a”,匹配项也是 “aaa”。

断言(Assertions)

字符 含义
x(?=y) 仅匹配被y跟随的x。
举个例子,/Jack(?=Sprat)/,如果”Jack”后面跟着sprat,则匹配之。/Jack(?=Sprat|Frost)/ ,如果”Jack”后面跟着”Sprat”或者”Frost”,则匹配之。但是,”Sprat” 和”Frost” 都不会在匹配结果中出现。
x(?!y) 仅匹配不被y跟随的x。
举个例子,/\d+(?!\.)/ 只会匹配不被点(.)跟随的数字。/\d+(?!\.)/.exec(‘3.141’) 匹配”141”,而不是”3.141”

对象方法(使用)

正则表达式可以被用于 RegExp 的exec和test方法以及 String 的match、replace、search和split方法。

方法 描述
exec 一个在字符串中执行查找匹配的RegExp方法,它返回一个数组(未匹配到则返回null)。传送门
test 一个在字符串中测试是否匹配的RegExp方法,它返回true或false。传送门
match 一个在字符串中执行查找匹配的String方法,它返回一个数组或者在未匹配到时返回null。传送门
search 一个在字符串中测试匹配的String方法,它返回匹配到的位置索引,或者在失败时返回-1。传送门
replace 一个在字符串中执行查找匹配的String方法,并且使用替换字符串替换掉匹配到的子字符串。传送门
split 一个使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中的String方法。传送门

这些方法详细的说明和使用方式建议点击表格中的传送门,到 W3C 中查看。

参考文献