正则
灵感胜于汗水 Lv5

正则表达式

正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。这些模式被用于 RegExpexectest 方法,以及 StringmatchmatchAllreplacesearchsplit 方法。

创建正则表达式

  1. 使用一个正则表达式字面量,其由包含在斜杠之间的模式组成

    1
    var re = /ab+c/;

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

  2. 或者调用RegExp对象的构造函数

    1
    var re = new RegExp("ab+c");

    在脚本运行过程中,用构造函数创建的正则表达式会被编译。如果正则表达式将会改变,或者它将会从用户输入等来源中动态地产生,就需要使用构造函数来创建正则表达式。

特殊字符

正则表达式中的特殊字符:

字符 含义
\ 依照下列规则匹配:在非特殊字符之前的反斜杠表示下一个字符是特殊字符,不能按照字面理解。
^ 匹配输入的开始。如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置。
$ 匹配输入的结束。如果多行标志被设置为 true,那么也匹配换行符前的位置。
* 匹配前一个表达式 0 次或多次。等价于 {0,}
+ 匹配前面一个表达式 1 次或者多次。等价于 {1,}
? 匹配前面一个表达式 0 次或者 1 次。等价于 {0,1}
.(小数点) 默认匹配除换行符之外的任何单个字符。
(x) 它会匹配 ‘x’ 并且记住匹配项。其中括号被称为捕获括号
(?:x) 匹配 ‘x’ 但是不记住匹配项。这种括号叫作非捕获括号
x(?=y) 匹配’x’仅仅当’x’后面跟着’y’.这种叫做先行断言。
(?<=y)x 匹配’x’仅当’x’前面是’y’.这种叫做后行断言。
x(?!y) 仅仅当’x’后面不跟着’y’时匹配’x’,这被称为正向否定查找。
x|y 匹配‘x’或者‘y’。
{n} n 是一个正整数,匹配了前面一个字符刚好出现了 n 次。
{n,} n 是一个正整数,匹配前一个字符至少出现了 n 次。
{n,m} n 和 m 都是整数。匹配前面的字符至少 n 次,最多 m 次。如果 n 或者 m 的值是 0,这个值被忽略。
[xyz] 一个字符集合。匹配方括号中的任意字符,包括转义序列。你可以使用破折号(-)来指定一个字符范围。对于点(.)和星号(*)这样的特殊符号在一个字符集中没有特殊的意义。
[^xyz] 一个反向字符集。也就是说, 它匹配任何没有包含在方括号中的字符。
[\b] 匹配一个退格 (U+0008)。(不要和\b混淆了。)
\b 匹配一个词的边界。一个词的边界就是一个词不被另外一个“字”字符跟随的位置或者前面跟其他“字”字符的位置,例如在字母和空格之间。
\B 匹配一个非单词的边界。
\cX 当 X 是处于 A 到 Z 之间的字符的时候,匹配字符串中的一个控制符。
\d 匹配一个数字。等价于 [0-9]
\D 匹配一个非数字字符。等价于 [^0-9]
\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 匹配一个非空白字符。
\t 匹配一个水平制表符 (U+0009)。
\v 匹配一个垂直制表符 (U+000B)。
\w 匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_]
\W 匹配一个非单字字符。等价于 [^A-Za-z0-9_]
\n 在正则表达式中,它返回最后的第 n 个子捕获匹配的子字符串 (捕获的数目以左括号计数)。
\0 匹配 NULL(U+0000)字符,不要在这后面跟其它小数,因为 \0<digits> 是一个八进制转义序列。
\xhh 匹配一个两位十六进制数(\x00-\xFF)表示的字符。
\uhhhh 匹配一个四位十六进制数表示的 UTF-16 代码单元。
\u{hhhh}或\u{hhhhh} (仅当设置了 u 标志时)匹配一个十六进制数表示的 Unicode 字符。

标志

正则表达式有六个可选参数 (flags) 允许全局和不分大小写搜索等。这些参数既可以单独使用也能以任意顺序一起使用,并且被包含在正则表达式实例中。

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

用法:

1
2
var re = /\w+\s/g
var re = new RegExp("\\w+\\s", "g")

如果使用 m 标志,^和$匹配的开始或结束输入字符串中的每一行,而不是整个字符串的开始或结束。

使用正则表达式

正则表达式可以被用于 RegExpexectest 方法,以及 StringmatchmatchAllreplacesearchsplit 方法。

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

exec

如果匹配失败,exec() 方法返回 null,并将正则表达式的 lastIndex 重置为 0。

如果匹配成功,exec() 方法返回一个数组,并更新正则表达式对象的 lastIndex 属性。

1
2
3
4
var myRe = new RegExp("d(b+)d", "g")
var myArray = myRe.exec("cdbbdbsbz")
console.log(myArray) // ['dbbd', 'bb', index: 1, input: 'cdbbdbsbz', groups: undefined]
console.dir(myRe) // 其中 lastIndex: 5

上述代码中,匹配成功后将返回一个数组并且更新正则表达式的属性。

myArray

  • 索引 0:表示最近一个匹配到的字符串。
  • 大于0的索引:对应所有被记住的子字符串。
  • index:匹配到的字符位于原始字符串的索引值。
  • input:初始字符串。
  • groups:一个命名捕获组对象,其键是名称,值是捕获组。若没有定义命名捕获组,则 groups 的值为 undefined

myRe

  • lastIndex:开始下一个匹配的起始索引值。(这个属性只有在使用 g 参数时可用)

命名捕获组

1
2
const date = '10-23-2022'
const newDate = date.replace(/(?<month>\d{2})-(?<day>\d{2})-(?<year>\d{4})/, "$<day>-$<month>-$<year>")

命名捕获分组很大的一个作用就是它能起到注释的作用。

名捕获分组自身的语法是 (?<name>...),比普通的分组多了一个 ?<name> 字样,其中 name 的起法就和你平时起变量名一样即可(不过在这里关键字也可用)。

反向引用一个命名分组的语法是 \k<name>,注意命名分组同样可以通过数字索引来反向引用,比如:

1
/(?<foo>a)\k<foo>\1/.test("aaa") // true

在 replace() 方法的替换字符串中反向引用是用 $<name>

1
"abc".replace(/(?<foo>a)/, "$<foo>-") // "a-bc",同样 $1 仍然可用

总结一下就是,和命名分组相关的有三种语法,分别是 ?<name>\k<name>$<name>,相同点是都用尖括号包裹着分组名。

string.match

  • 如果使用 g 标志,则将返回与完整正则表达式匹配的所有结果,但不会返回捕获组。
  • 如果未使用 g 标志,则仅返回第一个完整匹配及其相关的捕获组(Array)。在这种情况下,返回的项目将具有如下所述的其他属性。

如上所述,匹配的结果包含如下所述的附加特性。

  • groups: 一个命名捕获组对象,其键是捕获组名称,值是捕获组,如果未定义命名捕获组,则为 undefined
  • index: 匹配的结果的开始位置
  • input: 搜索的字符串。

一个Array,其内容取决于 global(g)标志的存在与否,如果未找到匹配则为null

如果正则表达式不包含 g 标志,str.match() 将返回与 RegExp.exec()相同的结果。

如果你想要获得捕获组,并且设置了全局标志,你需要用 RegExp.exec() 或者 String.prototype.matchAll()

string.matchAll

1
str.matchAll(regexp)

RegExp必须是设置了全局模式g的形式,否则会抛出异常TypeError

返回值:

一个迭代器(不可重用,结果耗尽需要再次调用方法,获取一个新的迭代器)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const regexp = RegExp('foo[a-z]*','g');
const str = 'table football, foosball';
let matches = str.matchAll(regexp);

// 使用for...of遍历
for (const match of matches) {
console.log(match);
}

matches = str.matchAll(regexp);
// 使用Array.from()
Array.from(matches, m => m[0])

matches = str.matchAll(regexp);
// 使用扩展运算符
const array = [...matches]
array[0] // ['football', index: 6, input: 'table football, foosball', groups: undefined]
array[1] // ['foosball', index: 16, input: 'table football, foosball', groups: undefined]

使用括号的子字符串匹配

一个正则表达式模式使用括号,将导致相应的子匹配被记住。

下面的脚本使用 replace() 方法来转换字符串中的单词。在匹配到的替换文本中,脚本使用替代的$1 ,$2 表示第一个和第二个括号的子字符串匹配。

1
2
3
4
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1");
console.log(newstr); // "Smith, John"
  • 本文标题:正则
  • 本文作者:灵感胜于汗水
  • 创建时间:2022-10-21 23:13:18
  • 本文链接:https://cjhsyc.github.io/2022/10/21/正则/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!