正则表达式(regular expression),用来匹配字符串的一种规则,常用来匹配、检查、替换字符。在 JavaScript 中可以长成像这样:
/^[a-z0-9]+$/i,它可以作为程序执行表达式的一部分,也可以在具备正则能力的工具里利用它,比如在 VSCode 的搜索里面用它来花式匹配、替换代码等。
正则表达式
正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。这些模式被用于 RegExp 的 exec 和 test 方法, 以及 String 的 match、matchAll、replace、search 和 split 方法。
例如/apple/i就代表了一个正则表达式,它的含义是匹配字符串中的“apple”字符,并且不区分大小写。
配合 String.prototype.replace 方法,我们可以用正则表达式来替换字符串中的内容。试着将字符串中的“apple”替换成“orange”:
const str = "Apple is red";
const reg = /apple/i;
const newStr = str.replace(reg, "Orange");
console.log(newStr); // "Orange is red"
创建正则表达式
字面量
最简单的方式是使用格式为/ *表达式主体* / *标志*这样的字面量来创建:
const reg = /^[a-z0-9]+$/i;
RegExp 对象
还可以调用RegExp对象的构造函数创建一个正则表达式对象:
const reg = new RegExp("^[a-z0-9]+$", "i");
正则表达式语法
从简单到复杂
一个表达式可能只是一个简单的字符匹配,也有可能是一个复杂的组合表达式。从匹配到组合的过程中,我们可以通过添加更多的条件来提高匹配的效率。
想要更灵活和高效就需要了解更多的规则,只要从匹配的**字符、数量、位置**三个方面入手,基本就可以掌控它。
字符
普通字符
正则表达式可以就好像我们开头说的“apple”一样,精确匹配想要的某几个普通字符。
特殊含义字符
有一些特定的字符或者字符组在正则表达式中是有特殊含义的。例如 \ 特殊含义是转义字符,跟在它后面的字符不再是普通字符,而是和\组成了一个特殊含义的标识,这个标识可能代表了一类字符。
如果遇见前面不是\,但是它却是特殊含义的字符时,想要获得它对应的普通字符匹配时,就需要在前面加上一个\。也就是如果要匹配\原本的“\”普通字符该怎么办?在它前面再加个转义符\就好了,/\\/就代表去匹配“\”。
| 字符 | 含义 |
|---|---|
. | 默认匹配除换行符之外的任何单个字符;如果修饰符的 s 开启的话,它也会匹配换行符 |
\w | 匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_] |
\W | 取反\w,等价于 [^A-Za-z0-9_] |
\d | 匹配一个数字,等价于 [0-9] |
\D | 取反\d,等价于 [^0-9] |
\s | 匹配一个空白字符,包括空格、制表符、换页符和换行符 |
\S | 取反\s |
\n | 匹配一个换行符 (U+000A) |
\r | 匹配一个回车符 (U+000D) |
\f | 匹配一个换页符 (U+000C) |
\t | 匹配一个水平制表符 (U+0009) |
\v | 匹配一个垂直制表符 (U+000B) |
\0 | 匹配 NULL(U+0000)字符, 不要在这后面跟其它小数,因为 \0 是一个八进制转义序列 |
\xhh | 匹配一个两位十六进制数(\x00-\xFF)表示的字符 |
\uhhhh | 匹配一个四位十六进制数表示的 UTF-16 代码单元 |
\u{hhhh}或\u{hhhhh} | (仅当设置了 u 标志时)匹配一个十六进制数表示的 Unicode 字符 |
范围性匹配
针对需要将字符限定在一定范围内的情况,有如下规则:
| 字符 | 含义 |
|---|---|
x|y | 匹配‘x’或者‘y’ |
[xyz] | 一个字符集合。匹配方括号中的任意字符,包括转义序列 |
[^xyz] | 取反 [xyz] |
[a-z] | 匹配字母 a 到 z |
[0-9] | 匹配数字 0 到 9 |
数量
贪婪
正则中有一些字符是用来限定字符出现的次数的。要注意像 * 和 + 这样的量词是“贪婪的”,这意味着它们试图匹配尽可能多的字符串。
| 字符 | 含义 |
|---|---|
* | 前面的字符出现 >=0 次 |
+ | 前面的字符出现 >=1 次 |
? | 前面的字符出现 0 或 1 次 |
{n} | 前面的字符出现 n 次, n 是一个正整数 |
{n,} | 前面的字符出现 >=n 次, n 是一个正整数 |
{n,m} | 前面的字符出现 n至m 次,n 和 m 都是整数 |
惰性
想要控制匹配不贪婪,需要再加一些限定。?量词后面的字符使量词“非贪婪”:意思是它一旦找到匹配就会停止。
有一段字符串 "okokook",分别用两个表达式匹配看一下结果:
/o.*k/结果是 "okokook" => 贪婪/o.*?k/结果是 "ok" => 惰性
位置
边界定位
^和$是边界定位符,它们可以用来限定匹配的字符串必须以某个字符开头或者结尾。
| 字符 | 含义 |
|---|---|
^ | 匹配输入的开始。如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置。 |
$ | 匹配输入的结束。如果多行标志被设置为 true,那么也匹配换行符前的位置。 |
\b | 匹配单词边界 |
\B | 匹配非单词边界 |
关于\b:
匹配一个词的边界。一个词的边界就是一个词不被另外一个“字”字符跟随的位置或者前面跟其他“字”字符的位置,例如在字母和空格之间。注意,匹配中不包括匹配的字边界。换句话说,一个匹配的词的边界的内容的长度是 0。(不要和[\b]混淆了)
使用"moon"举例:
/\bm/匹配“moon”中的‘m’;
/oo\b/并不匹配"moon"中的'oo',因为'oo'被一个“字”字符'n'紧跟着。
/oon\b/匹配"moon"中的'oon',因为'oon'是这个字符串的结束部分。这样他没有被一个“字”字符紧跟着。
/\w\b\w/将不能匹配任何字符串,因为在一个单词中间的字符永远也不可能同时满足没有“字”字符跟随和有“字”字符跟随两种情况。
关于\b:
匹配一个非单词边界。匹配如下几种情况:
- 字符串第一个字符为非“字”字符
- 字符串最后一个字符为非“字”字符
- 两个单词字符之间
- 两个非单词字符之间
- 空字符串
例如,/\B../匹配"noonday"中的'oo', 而/y\B../匹配"possibly yesterday"中的’yes‘
位置关系
当存在需要查找某段字符前面或后面的字符的情况,可能需要使用下面的规则:
请注意以下 exp2 表示与 exp1 存在位置关系的表达式, exp1 才是目标表达式
| 字符 | 含义 |
|---|---|
exp1(?=exp2) | 匹配后面是 exp2 的 exp1 |
exp1(?!exp2) | 匹配后面不是 exp2 的 exp1 |
(?<=exp2)exp1 | 匹配前面是 exp2 的 exp1 |
(?<!exp2)exp1 | 匹配前面不是 exp2 的 exp1 |
组及相关
| 字符 | 含义 |
|---|---|
(x) | 捕获组 |
\n | 其中 n 是一个正整数。对正则表达式中与 n 括号匹配的最后一个子字符串的反向引用(计算左括号) |
(?<Name>x) | 具名捕获组: 匹配"x"并将其存储在返回的匹配项的 groups 属性中,该属性位于指定的名称下。尖括号(< 和 >) 用于组名 |
(?:x) | 非捕获组: 匹配 “x”,但不记得匹配 |
关于组的细节请参考MDN Groups and ranges
修饰符
正则表达式有六个可选参数 (flags) 允许全局和不分大小写搜索等。这些参数既可以单独使用也能以任意顺序一起使用, 并且被包含在正则表达式实例中。
| 标志 | 含义 |
|---|---|
i | 全局搜索。 |
m | 不区分大小写搜索。 |
g | 多行搜索。 |
s | 允许 . 匹配换行符。 |
u | 使用 unicode 码的模式进行匹配。 |
y | 执行“粘性(sticky)”搜索,匹配从目标字符串的当前位置开始。 |
Unicode 属性转义
请参考 MDN Unicode 属性转义
常用正则表达式
| 用途 | 正则表达式 |
|---|---|
| vscode 匹配一般 console.log | (?<!=>\s)(\/\/?\s*)?console.log\((.*)\);? |
| vscode 匹配一般空行 | ^\s*(?=\r?$)\n |
可参考第三方开源整理库 any86.github.io/any-rule/