JavaScript 中的正则表达式

字数统计: 3.8k阅读时长: 14 min
2020/07/10 Share

在JavaScript中,正则表达式是一个对象。相关的一些方法是 RegExpexectest ,以及StringmatchmatchAllreplacesearchsplit

字符分类

字符分为转义字符和非转义字符。

转义字符

左斜杠 \ ,再任意字符前面加上它,即是一个转义字符,包括它自己。

eg: \n 表示 换行符, \r 表示回车符

非转义字符

转义字符之外的字符

普通字符

平时所用的英文字母,数字标点等,没有特殊含义

元字符

有些字符有自己的特殊含义,如 ^ $ 等位置限定符,还有 * + 等数量限定符

正则表达式

字面量字符

即匹配的是一个固定字符,这时候其实也可以用 includes 或者 indexOf 方法解决。

1
2
3
let reg = /cooperhu/;
let str = 'cooperhu great';
reg.test(str); // true

字符组

如果需求不是匹配一个固定字符串,而是匹配组合的情况,在方括号里的内容是或的关系。

1
2
3
4
5
let reg = /[abc]/;
reg.test('a'); // true
reg.test('b'); //true
reg.test('c'); // true
reg.test('abc'); // true

但是要是匹配任一英文字母总不能[abcdefghijklmnopqrstuvwxyz]吧,这样也太长了,也不美观。

这时候可以使用范围表示法 -

  • [a-z]表示 a-z任一个小写字母
  • [A-Z]表示任一大写字母
  • [0-9]表示0到9中的任一数字

如果要表示一个字符是否是 数字,字母或者下划线就可以写为:

1
let reg = /[a-zA-Z0-9_]/;

但是这样写有时候也很麻烦,正则提供了更简单的写法,可以直接写为 \w

排除字符组: /[^abc]/ 指目标字符串不能包含a或b或c,这个^不用在字符组里是开始定位符,需要特殊记一下。

量词

量词是对字符出现次数进行限定,例如长度和重复次数等。

符号 功能
* 0次或者多次
+ 1次或者多次
0次或者1次
{n} 指定n次
{n,} 至少n次
{n, m} 至少n次,最多m次
{, m} 最多m次

贪婪量词和惰性量词

1
2
3
4
let reg = /\d{1,3}/;
let regLazy = /\d{1,3}?/
'12 123 1234'.match(reg); // ["12", index: 0, input: "12 123 1234", groups: undefined]
'12 123 1234'.match(regLazy); // ["1", index: 0, input: "12 123 1234", groups: undefined]

正常的量词匹配时贪婪的,能多匹配尽量多匹配,相反地如果想实现惰性匹配需要在后面增加一个?

位置匹配

常见的几种位置限定符号

符号 功能
^ 以什么开头
$ 以什么结尾
\b 匹配一个单词边界,即字与空格的位置
\B 非单词边界匹配
(?=p) 字符p前面的位置,或者当前位置后面必须是字符p
(?!p) 与(?=p)相反

分组

分组通过括号来实现,一个括号内的表达式就是一个子表达式,我们可以自字符串中直接引用分组。

1
2
//yyyy-mm-dd要变成 yyyy/mm/dd的格式
date.replace(/(\d{4})-(\d{2})-(\d{2})/g, '$1/$2/$3')

生成的分组除了在字符串中引用,我们还可以在正则表达式中引用,且只能引用前面的分组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var string1 = "2017-06-12";
var string2 = "2017/06/12";
var string3 = "2017.06.12";
var string4 = "2016-06/12";
let reg = /\d{4}-|\/|\.\d{2}-|\/|\.\d{2}/
console.log(reg.test(string1));//true
console.log(reg.test(string2));//true
console.log(reg.test(string3));//true
console.log(reg.test(string4));//true
// 以上方法只是在每个位置都把- 或者/或者. 这三种情况用分支选择列出来,这样可以满足我们目前的需求,但是如果要求前后连接符必须一致呢?
let reg = /\d{4}(-|\/|\.)\d{2}\1\d{2}/
console.log(reg.test(string1));//true
console.log(reg.test(string2));//true
console.log(reg.test(string3));//true
console.log(reg.test(string4));//false

正则表达式特殊字符表格(MDN)

字符 含义
\ 依照下列规则匹配:在非特殊字符之前的反斜杠表示下一个字符是特殊字符,不能按照字面理解。例如,前面没有 “" 的 “b” 通常匹配小写字母 “b”,即字符会被作为字面理解,无论它出现在哪里。但如果前面加了 “",它将不再匹配任何字符,而是表示一个字符边界。在特殊字符之前的反斜杠表示下一个字符不是特殊字符,应该按照字面理解。详情请参阅下文中的 “转义(Escaping)” 部分。如果你想将字符串传递给 RegExp 构造函数,不要忘记在字符串字面量中反斜杠是转义字符。所以为了在模式中添加一个反斜杠,你需要在字符串字面量中转义它。/[a-z]\s/inew RegExp("[a-z]\\s", "i") 创建了相同的正则表达式:一个用于搜索后面紧跟着空白字符(\s 可看后文)并且在 a-z 范围内的任意字符的表达式。为了通过字符串字面量给 RegExp 构造函数创建包含反斜杠的表达式,你需要在字符串级别和正则表达式级别都对它进行转义。例如 /[a-z]:\\/inew RegExp("[a-z]:\\\\","i") 会创建相同的表达式,即匹配类似 “C:" 字符串。
^ 匹配输入的开始。如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置。例如,/^A/ 并不会匹配 “an A” 中的 ‘A’,但是会匹配 “An E” 中的 ‘A’。当 ‘^‘ 作为第一个字符出现在一个字符集合模式时,它将会有不同的含义。反向字符集合 一节有详细介绍和示例。
$ 匹配输入的结束。如果多行标志被设置为 true,那么也匹配换行符前的位置。例如,/t$/ 并不会匹配 “eater” 中的 ‘t’,但是会匹配 “eat” 中的 ‘t’。
* 匹配前一个表达式 0 次或多次。等价于 {0,}。例如,/bo*/ 会匹配 “A ghost boooooed” 中的 ‘booooo’ 和 “A bird warbled” 中的 ‘b’,但是在 “A goat grunted” 中不会匹配任何内容。
+ 匹配前面一个表达式 1 次或者多次。等价于 {1,}。例如,/a+/ 会匹配 “candy” 中的 ‘a’ 和 “caaaaaaandy” 中所有的 ‘a’,但是在 “cndy” 中不会匹配任何内容。
? 匹配前面一个表达式 0 次或者 1 次。等价于 {0,1}。例如,/e?le?/ 匹配 “angel” 中的 ‘el’、”angle” 中的 ‘le’ 以及 “oslo’ 中的 ‘l’。如果紧跟在任何量词 *、 +、? 或 {} 的后面,将会使量词变为非贪婪(匹配尽量少的字符),和缺省使用的贪婪模式(匹配尽可能多的字符)正好相反。例如,对 “123abc” 使用 /\d+/ 将会匹配 “123”,而使用 /\d+?/ 则只会匹配到 “1”。还用于先行断言中,如本表的 x(?=y)x(?!y) 条目所述。
. (小数点)默认匹配除换行符之外的任何单个字符。例如,/.n/ 将会匹配 “nay, an apple is on the tree” 中的 ‘an’ 和 ‘on’,但是不会匹配 ‘nay’。如果 s (“dotAll”) 标志位被设为 true,它也会匹配换行符。
(x) 像下面的例子展示的那样,它会匹配 ‘x’ 并且记住匹配项。其中括号被称为捕获括号。模式 /(foo) (bar) \1 \2/ 中的 ‘(foo)‘ 和 ‘(bar)‘ 匹配并记住字符串 “foo bar foo bar” 中前两个单词。模式中的 \1\2 表示第一个和第二个被捕获括号匹配的子字符串,即 foobar,匹配了原字符串中的后两个单词。注意 \1\2、…、\n 是用在正则表达式的匹配环节,详情可以参阅后文的 \n 条目。而在正则表达式的替换环节,则要使用像 $1$2、…、$n 这样的语法,例如,'bar foo'.replace(/(...) (...)/, '$2 $1')$& 表示整个用于匹配的原字符串。
(?:x) 匹配 ‘x’ 但是不记住匹配项。这种括号叫作非捕获括号,使得你能够定义与正则表达式运算符一起使用的子表达式。看看这个例子 /(?:foo){1,2}/。如果表达式是 /foo{1,2}/{1,2} 将只应用于 ‘foo’ 的最后一个字符 ‘o’。如果使用非捕获括号,则 {1,2} 会应用于整个 ‘foo’ 单词。更多信息,可以参阅下文的 Using parentheses 条目.
x(?=y) 匹配’x’仅仅当’x’后面跟着’y’.这种叫做先行断言。例如,/Jack(?=Sprat)/会匹配到’Jack’仅当它后面跟着’Sprat’。/Jack(?=Sprat|Frost)/匹配‘Jack’仅当它后面跟着’Sprat’或者是‘Frost’。但是‘Sprat’和‘Frost’都不是匹配结果的一部分。
(?<=y)x 匹配’x’仅当’x’前面是’y’.这种叫做后行断言。例如,/(?<=Jack)Sprat/会匹配到’ Sprat ‘仅仅当它前面是’ Jack ‘。/(?<=Jack|Tom)Sprat/匹配‘ Sprat ’仅仅当它前面是’Jack’或者是‘Tom’。但是‘Jack’和‘Tom’都不是匹配结果的一部分。
x(?!y) 仅仅当’x’后面不跟着’y’时匹配’x’,这被称为正向否定查找。例如,仅仅当这个数字后面没有跟小数点的时候,/\d+(?!.)/ 匹配一个数字。正则表达式/\d+(?!.)/.exec(“3.141”)匹配‘141’而不是‘3.141’
(?<!*y*)*x* 仅仅当’x’前面不是’y’时匹配’x’,这被称为反向否定查找。例如, 仅仅当这个数字前面没有负号的时候,/(?<!-)\d+/ 匹配一个数字。 /(?<!-)\d+/.exec('3') 匹配到 “3”. /(?<!-)\d+/.exec('-3') 因为这个数字前有负号,所以没有匹配到。
[`x y`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions#special-or)
{n} n 是一个正整数,匹配了前面一个字符刚好出现了 n 次。 比如, /a{2}/ 不会匹配“candy”中的’a’,但是会匹配“caandy”中所有的 a,以及“caaandy”中的前两个’a’。
{n,} n是一个正整数,匹配前一个字符至少出现了n次。例如, /a{2,}/ 匹配 “aa”, “aaaa” 和 “aaaaa” 但是不匹配 “a”。
{n,m} n 和 m 都是整数。匹配前面的字符至少n次,最多m次。如果 n 或者 m 的值是0, 这个值被忽略。例如,/a{1, 3}/ 并不匹配“cndy”中的任意字符,匹配“candy”中的a,匹配“caandy”中的前两个a,也匹配“caaaaaaandy”中的前三个a。注意,当匹配”caaaaaaandy“时,匹配的值是“aaa”,即使原始的字符串中有更多的a。
[xyz\] 一个字符集合。匹配方括号中的任意字符,包括转义序列。你可以使用破折号(-)来指定一个字符范围。对于点(.)和星号(*)这样的特殊符号在一个字符集中没有特殊的意义。他们不必进行转义,不过转义也是起作用的。 例如,[abcd] 和[a-d]是一样的。他们都匹配”brisket”中的‘b’,也都匹配“city”中的‘c’。/[a-z.]+/ 和/[\w.]+/与字符串“test.i.ng”匹配。
[^xyz\] 一个反向字符集。也就是说, 它匹配任何没有包含在方括号中的字符。你可以使用破折号(-)来指定一个字符范围。任何普通字符在这里都是起作用的。例如,[^abc] 和 [^a-c] 是一样的。他们匹配”brisket”中的‘r’,也匹配“chop”中的‘h’。
[\b\] 匹配一个退格(U+0008)。(不要和\b混淆了。)
\b 匹配一个词的边界。一个词的边界就是一个词不被另外一个“字”字符跟随的位置或者前面跟其他“字”字符的位置,例如在字母和空格之间。注意,匹配中不包括匹配的字边界。换句话说,一个匹配的词的边界的内容的长度是0。(不要和[\b]混淆了)使用”moon”举例: /\bm/匹配“moon”中的‘m’; /oo\b/并不匹配”moon”中的’oo’,因为’oo’被一个“字”字符’n’紧跟着。 /oon\b/匹配”moon”中的’oon’,因为’oon’是这个字符串的结束部分。这样他没有被一个“字”字符紧跟着。 /\w\b\w/将不能匹配任何字符串,因为在一个单词中间的字符永远也不可能同时满足没有“字”字符跟随和有“字”字符跟随两种情况。注意: JavaScript的正则表达式引擎将特定的字符集定义为“字”字符。不在该集合中的任何字符都被认为是一个断词。这组字符相当有限:它只包括大写和小写的罗马字母,十进制数字和下划线字符。不幸的是,重要的字符,例如“é”或“ü”,被视为断词。
\B 匹配一个非单词边界。匹配如下几种情况:字符串第一个字符为非“字”字符字符串最后一个字符为非“字”字符两个单词字符之间两个非单词字符之间空字符串例如,/\B../匹配”noonday”中的’oo’, 而/y\B../匹配”possibly yesterday”中的’yes‘
\c*X* 当X是处于A到Z之间的字符的时候,匹配字符串中的一个控制符。例如,/\cM/ 匹配字符串中的 control-M (U+000D)。
\d 匹配一个数字。``等价于[0-9]。例如, /\d/ 或者 /[0-9]/ 匹配”B2 is the suite number.”中的’2’。
\D 匹配一个非数字字符。``等价于[^0-9]。例如, /\D/ 或者 /[^0-9]/ 匹配”B2 is the suite number.”中的’B’ 。
\f 匹配一个换页符 (U+000C)。
\n 匹配一个换行符 (U+000A)。
\r 匹配一个回车符 (U+000D)。
\s 匹配一个空白字符,包括空格、制表符、换页符和换行符。等价于[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。例如, /\s\w*/ 匹配”foo bar.”中的’ bar’。经测试,\s不匹配”\u180e“,在当前版本Chrome(v80.0.3987.122)和Firefox(76.0.1)控制台输入/\s/.test(“\u180e”)均返回false。
\S 匹配一个非空白字符。等价于 [^\f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。例如,/\S\w*/ 匹配”foo bar.”中的’foo’。
\t 匹配一个水平制表符 (U+0009)。
\v 匹配一个垂直制表符 (U+000B)。
\w 匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_]。例如, /\w/ 匹配 “apple,” 中的 ‘a’,”$5.28,”中的 ‘5’ 和 “3D.” 中的 ‘3’。
\W 匹配一个非单字字符。等价于 [^A-Za-z0-9_]。例如, /\W/ 或者 /[^A-Za-z0-9_]/ 匹配 “50%.” 中的 ‘%’。
\*n* 在正则表达式中,它返回最后的第n个子捕获匹配的子字符串(捕获的数目以左括号计数)。比如 /apple(,)\sorange\1/ 匹配”apple, orange, cherry, peach.”中的’apple, orange,’ 。
\0 匹配 NULL(U+0000)字符, 不要在这后面跟其它小数,因为 \0<digits> 是一个八进制转义序列。
\xhh 匹配一个两位十六进制数(\x00-\xFF)表示的字符。
\uhhhh 匹配一个四位十六进制数表示的 UTF-16 代码单元。
\u{hhhh}或\u{hhhhh} (仅当设置了u标志时)匹配一个十六进制数表示的 Unicode 字符。
CATALOG
  1. 1. 字符分类
    1. 1.1. 转义字符
    2. 1.2. 非转义字符
      1. 1.2.1. 普通字符
      2. 1.2.2. 元字符
  2. 2. 正则表达式
    1. 2.1. 字面量字符
    2. 2.2. 字符组
    3. 2.3. 量词
      1. 2.3.1. 贪婪量词和惰性量词
    4. 2.4. 位置匹配
    5. 2.5. 分组
  3. 3. 正则表达式特殊字符表格(MDN)