【JavaScript】正则表达式语法

2021/09/08 18:10:45

正则语法速查表

正则语法速查表

正则表达式中的特殊字符

断言

表示一个匹配在某些条件下发生。断言包含先行断言、后行断言和条件表达式。

  • ^

    匹配输入的开头。如果多行模式设为 true,^ 在换行符后也能立即匹配,比如 /^A/ 匹配不了 "an A" 里面的 "A",但是可以匹配 "An A" 里面第一个 "A"。

    注意与 [^xyz] 的区别。

  • $

    匹配输入的结束。如果多行模式设为 true,^ 在换行符前也能立即匹配,比如 /t$/ 不能匹配 "eater" 中的 "t",但是可以匹配 "eat" 中的 "t"。

  • \b

    匹配一个单词的边界,这是一个字的字符前后没有另一个字的字符位置, 例如在字母和空格之间。需要注意的是匹配的单词边界不包括在匹配中。换句话说,匹配字边界的长度为零。

    • /\bm/ 在 "moon" 中匹配到 "m"。
    • /oo\b/ 在 "moon" 中不会匹配到 "oo", 因为 "oo" 后面跟着 "n" 这个单词字符。
    • /oon\b/ 在 "moon" 中匹配 "oon", 因为 "oon" 是这个字符串的结尾, 因此后面没有单词字符。
    • /\w\b\w/ 将永远不会匹配任何东西,因为一个单词字符后面永远不会有非单词字符和单词字符。

    注意与 [\b] 的区别

  • \B

    匹配非单词边界,这是上一个字符和下一个字符属于同一类型的位置:要么两者都必须是单词,要么两者都必须是非单词。

  • x(?=y)

    向前断言,x 后紧随 y 时匹配 x,匹配结果不包括 y。

  • x(?!y)

    向前否定断言,x 后没有紧随 y 时匹配 x,匹配结果不包括 y。

  • (?<=y)x

    向后断言,x 紧随 y 时匹配 x,匹配结果不包括 y。

  • (?<!y)x

    向后否定断言,x 不紧随 y 时匹配 x,匹配结果不包括 y。

字符

区分不同类型的字符,例如区分字母和数字。

字符类——MDNopen in new window

  • .

    匹配除行终止符之外的任何单个字符/.y/"yes make my day" 中匹配的是 myay,换成 /..y/ 匹配的则是 my(注意空格) 和 day

  • \d & \D

    \d 匹配数字,相当于 [0-9]

    \D 匹配非数,相当于 [^0-9]

  • \w & \W

    \w 匹配字符,包括下划线,相当于 [A-Za-z0-9_]

    \W 匹配非字符,相当于 [^A-Za-z0-9_]

  • \s & \S

    \s 匹配一个空白字符,包括空格、制表符、换行符和其他 Unicode 空格,相当于 [ \f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]

    \S 匹配非空白字符 [^ \f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]

  • 其他字符 \t 匹配水平制表符(tab) \r 匹配回车 \n 匹配换行 \v 匹配垂直制表符open in new window \f 匹配分页符 [\b] 匹配退格,注意与断言类 \b 的区别 \0 匹配 Null Character(空字符)open in new window,后不能跟数字,在 js 中未找到匹配该规则的字符串 \xhh 匹配编码 hh(两个十六进制数字)的字符。 \uhhhh 用值 hhhh(4 个十六进制数字)匹配 UTF-16 编码。

组和范围

  • x | y

    匹配 x 或 y 。

  • [xyz][a-c]

    匹配任何包含的字符。可以使用连字符来指定字符范围,但如果连字符是方括号的第一个或最后一个字符,则它将被作为普通字符包含在字符集中的文字连字符。

    例如, [abcd][a-d] 是一样的,[-abcd] 会匹配 non-profit 中的 -

  • [^xyz] [^z-c]

    匹配任何没有包含在括号中的字符。

  • (x)

    捕获组,匹配 x 并记住匹配项。

    正则表达式可以有多个捕获组,捕获组的顺序是正则中左括号的顺序。

    括号并不会影响其他规则,如:/[abc]//[a(b)c]/ 都可以匹配 字符串中的 a、b、c,区别在于使用了括号后可以通过 \n 来引用捕获组中的内容。

  • \n

    引用第 n 个捕获组的内容,使用形式为 \1、\2,表示引用第一个、第二个捕获组匹配的内容。序号从 0 开始,注意 \0 是另外的正则语法。

    注意与字符类 \n 的区别。

  • (?<Name>x)

    具名捕获组,匹配"x"并将其存储在返回的匹配项的 groups 属性中,'web-doc'.match(/-(?<customName>\w)/).groups //{customName: "d"}

  • (?:x)

    非捕获组,匹配 x 但是不记录在捕获组中,即无法用 \n 引用也不记录在捕获组对象的 groups 中。适用于有多个捕获组时忽略掉一些捕获组以更好的使用 \n 的情况,js 中的正则有具名捕获组的概念,所以这个语法只能限制 \n 的使用?

量词

  • x*

    将前面的项 x 匹配 0 次或多次。

  • x +

    将前面的项 x 匹配 1 次或多次,等价于 {1,}(见下文)。

  • x?

    将前面的项 x 匹配 0 或 1 次。

    注意在量词(包括自己,也就是可以有 x?? 这样的用法)后使用时的含义变化(非贪婪匹配)。

  • x

    将前面的项 x 匹配 n 次,n 为整数。

  • x

    将前面的项 x 匹配至少 n 次。

  • x

    将前面的项匹配 [n,m] 次

量词的贪婪匹配与非贪婪匹配

量词默认情况下是贪婪匹配,如果在任何量词*、+、?或{}之后使用?,则使量词进行非贪婪匹配。

贪婪匹配:匹配尽可能多的字符串。 非贪婪匹配:一旦找到匹配对象就立即停止。

如:'aaaaaaaaaaaa'.match(/a{2,5}/g); // ["aaaaa", "aaaaa", "aa"],使用非贪婪匹配后则是:'aaaaaaaaaaaa'.match(/a{2,5}?/g); // ["aa", "aa", "aa", "aa", "aa", "aa"]

Unicode 属性转义

使用 Unicode 属性可以匹配表情、标点符号、字母(甚至适用特定语言或文字)等。

Unicode 属性转义——MDNopen in new window

正则表达式标志

标志描述
g全局搜索。
i不区分大小写搜索。
m多行搜索。
s允许 . 匹配换行符。
u使用 unicode 码的模式进行匹配。
y执行“粘性(sticky)”搜索,匹配从目标字符串的当前位置开始。

使用正则表达式

如果只是为了判断是否匹配(true 或 false),可以使用 RegExp.test() 或者 String.search() 方法。

如果想获取所有匹配项可以用 String.match() 方法,注意要添加全局标志(g)。

如果想查看每次匹配的详情可以用 RegExp.exec() 方法(注意全局标志的影响),可以通过捕获组的 index 和每次调用后的 RegExp.lastIndex 可以确定这次匹配的开始和结束位置。

如果想获取所有匹配项的捕获组可以用 String.matchAll() 方法,注意要添加全局标志(g)。无法监控 RegExp.lastIndex 的状态。

RegExp.prototype.exec(str)

使用全局标志:字符串中有匹配项时返回这条匹配信息的捕获组,同时更新 RegExp.lastIndex 属性;不匹配不匹配时返回 null,同时 RegExp.lastIndex 属性设置为 0。

不使用全局标志:返回第一个匹配项的捕获组或 null。

如果要获得所有匹配的捕获组可以使用 String.prototype.matchAll(reg) 方法。

通过捕获组的 index 和每次调用后的 RegExp.lastIndex 可以确定这次匹配的开始和结束位置。

var str = "我今年25岁明年26岁后年27岁千年24岁";

var reg1 = /\d+/g;
var reg2 = /\d+/;

// 使用global或sticky标志在下次调用exec时会从上一次匹配位置(lastIndex)之后进行匹配
reg1.exec(str); // ["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
reg1.exec(str); // ["26", index: 8, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
reg1.exec(str); // ["27", index: 13, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
reg1.exec(str); // ["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
reg1.exec(str); // null

// 没有使用global或sticky标志则不会记录状态
reg2.exec(str); // ["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
reg2.exec(str); // ["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]

注意,即使再次查找的字符串不是原查找字符串时,lastIndex 也不会被重置,它依旧会从记录的 lastIndex 开始。

let str1 = "我今年25岁明年26岁后年27岁千年24岁";
let str2 = "我今年25岁明年26岁后年27岁千年24岁";
let reg = /\d+/g;

reg.exec(str1); // ["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
reg.lastIndex; // 5
reg.exec(str1); // ["26", index: 8, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
reg.lastIndex; // 10
reg.exec(str2); // ["27", index: 13, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
reg.lastIndex; // 15
reg.exec(str2); // ["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
reg.lastIndex; // 20
reg.exec(str1); // null
reg.lastIndex; // 0

匹配成功后更新 lastIndex 属性,匹配失败时将 lastIndex 重置为 0。

RegExp.prototype.test(str)

查看正则表达式与指定的字符串是否匹配,返回 true 或 false。

类似于 String.prototype.search() 方法,差别在于 test 返回一个布尔值,而 search 返回索引(如果找到)或者-1(如果没找到)。

如果正则表达式设置了全局标志,test() 的执行会改变正则表达式 lastIndex 属性。连续的执行 test()方法,后续的执行将会从 lastIndex 处开始匹配字符串。

同 exec 一样,匹配成功后更新 lastIndex 属性,匹配失败时将 lastIndex 重置为 0。

var str = "我今年25岁明年26";

var reg1 = /\d+/g;
var reg2 = /\d+/;

// 使用global或sticky标志在下次调用test时会从上一次匹配位置(lastIndex)之后进行匹配
reg1.test(str); // true
reg1.test(str); // true
reg1.test(str); // false

// 没有使用global或sticky标志则不会记录状态
reg1.test(str); // true
reg1.test(str); // true
reg1.test(str); // true

String.prototype.match(reg)

如果使用全局标志,则返回与正则表达式匹配的所有结果的数组。

如果未使用全局标志,则仅返回第一个完整匹配及其相关的捕获组(Array),其返回值与不带全局标志的 reg.exec() 的返回值相同。

var str = "我今年25岁明年26岁后年27岁千年24岁";
var reg1 = /\d+/g;
var reg2 = /\d+/;

// 使用全局标志则返回所有匹配项
str.match(reg1); // ["25", "26", "27", "24"]

// 没有全局标志返回第一个匹配项的[捕获组](编程基础\WebAPI\ECMAScript\对象\RegExp.md#捕获组),不会修改lastIndex属性
str.match(reg2); // ["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
str.match(reg2); // ["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]

String.prototype.matchAll(reg)

返回匹配捕获组的迭代器,这里的正则必须带全局标志,否则会报错。

这里的返回结果与依次调用 RegExp.exec() 方法得到的结果一致。

使用 matchAll 方法无法监控 RegExp.lastIndex 的变化。

var str = "我今年25岁明年26岁后年27岁千年24岁";
var reg1 = /\d+/g;
var arr = [...str.matchAll(reg1)];
// [
//     ["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
//     ["26", index: 8, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
//     ["27", index: 13, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
//     ["24", index: 18, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]
// ]

String.prototype.search(reg)

返回正则表达式在字符串中首次匹配项的索引;无匹配则返回 -1。全局标志对此方法无影响。

var str = "我今年25岁明年26";

var reg1 = /\d+/g;
var reg2 = /\d+/;

// 全局标志对结果无影响
str.search(reg1); // 3
str.search(reg1); // 3

String.prototype.replace(regexp|substr, newSubStr|function)

特殊变量名

二参为字符串时可设置以下特殊字符串

变量名代表的值
$$插入一个 "$"。
$&`插入匹配的子串。
$`插入当前匹配的子串左边的内容。
$'插入当前匹配的子串右边的内容。
$n假如第一个参数是 RegExp 对象,并且 n 是个小于 100 的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从 1 开始。如果不存在第 n 个分组,那么将会把匹配到到内容替换为字面量。比如不存在第 3 个分组,就会用“$3”替换匹配到的内容。
$<Name>这里 `Name 是一个分组名称。如果在正则表达式中并不存在分组(或者没有匹配),这个变量将被处理为空字符串。只有在支持命名分组捕获的浏览器中才能使用。

指定函数作为参数

二参为函数时接收以下参数

变量名代表的值
match匹配的子串。
p1,p2, ...假如 replace()方法的第一个参数是一个 RegExp 对象,则代表第 n 个括号匹配的字符串。(对应于上述的$1,$2 等。)例如,如果是用 /(\a+)(\b+)/ 这个来匹配,p1 就是匹配的 \a+p2 就是匹配的 \b+
offset匹配到的子字符串在原字符串中的偏移量,也就是索引。(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',那么这个参数将会是 1)
string被匹配的原字符串。
NamedCaptureGroup命名捕获组匹配的对象

String.prototype.split(regexp|substr, limit?)

分隔字符串,二参为限定分隔的次数。

其他概念

RegExp.lastIndex

lastIndex 是正则表达式的一个可读可写的整型属性,用来指定下一次匹配的起始索引。

lastIndex 表示上次成功匹配的最后位置。

会修改 reg.lastIndex 属性(设置全局标志)的 API 包括:RegExp.exec()、RegExp.test()。

捕获组

一个捕获组的数据格式为:["25", index: 3, input: "我今年25岁明年26岁后年27岁千年24岁", groups: undefined]

在设置了 global 或 sticky 标志位的情况下(如 /foo/g or /foo/y),JavaScript RegExp 对象是有状态的。他们会将上次成功匹配后的位置记录在 lastIndex 属性(当前 RegExp 表达式的属性,即 reg.lastIndex)中。

  • 前面的字符串表示匹配的字符串和捕获组匹配的字符串
  • index: 匹配的结果的开始位置。
  • input: 完整字符串。
  • groups: 一个具名捕获组数组,没有则为 null。

规则

只有正则表达式使用了表示全局检索的 "g" 标志时,该属性才会起作用。此时应用下面的规则:

  • 如果 lastIndex 大于字符串的长度,则 regexp.test 和 regexp.exec 将会匹配失败,然后 lastIndex 被设置为 0。
  • 如果 lastIndex 等于字符串的长度,且该正则表达式匹配空字符串,则该正则表达式匹配从 lastIndex 开始的字符串。
  • 如果 lastIndex 等于字符串的长度,且该正则表达式不匹配空字符串 ,则该正则表达式不匹配字符串,lastIndex 被设置为 0.。
  • 否则,lastIndex 被设置为紧随最近一次成功匹配的下一个位置。

参考

正则表达式open in new window