Skip to content

正则表达式

问题:正则表达式的使用

关于正则表达式,之前阅读了《正则指引》,并进行了相关的整理,下面是简单的总结回顾。

基本语法

字符组

字符组[]中括号包围的字符集合,表示“在同一个位置可能出现的各种字符“

  • 字符组中多个连续字符可以使用-范围表示法,根据字符的ASCII编码,编码小的字符在前,编码大的字符在后
  • 正则表达式所使用的一些字符,比如^,$,-等,需要通过转义符\转义才可以使用
  • 排除行字符组[^],表示"在当前位置,匹配一个没有列出的字符"
  • \d数字(digit) 和 \D非数字,\w字符(word) 和 \W非字符,\s空白(space) 和 \S非空白符
  • .通配符可以用来匹配除换行符外的全部字符,如果需要匹配真正的全部字符,可以使用[\w\W][^]

量词

量词则用来匹配多个字符,使用{m,n}来表示某个数量范围内的量词,此外还有一些简写形式

  • +表示{1,}一个或多个
  • *表示{0,}0个或多个
  • ?表示{0,1}0个或一个

通用字符.与量词组合就可以匹配任意长度的字符,这里需要区分贪婪匹配和非贪婪匹配

  • .*属于贪婪匹配,贪婪匹配会尽可能的匹配较多的符合.的字符(遇见换行符或者字符串结束)为止,然后根据正则表达式中的下一个字符(如果存在),从匹配到的结果中从后向前回溯,直至遇见第一个字符为止
  • .*?属于非贪婪匹配,其原理是每遇见一个.能匹配的字符就优先进行忽略,并直接匹配?后面的那个字符,如果匹配不成功则将前面的字符记录并尝试匹配下一个字符,如果在某次对?后面的那个字符匹配成功之后,则直接返回记录的字符串

表达式

使用()来定义一个表达式,通常被称为正则表达式的子表达式,子表达式作为一个整体参与匹配。

  • 可以在括号内以|竖线分隔开多个子表达式
  • 使用括号之后,正则表达式会保存每个分组真正匹配的文本,这种功能被称为捕获分组,在拿到匹配结果后,可以通过分组的编号来获取捕获内容,
    • 分组的编号都是根据开括号从左向右出现的顺序来计数的
    • 如果不需要获取相应的子表达式,为了提高性能,可以显式地声明非捕获分组。 非捕获分组使用(?:)来进行声明
  • 在正则表达式匹配过程中也可以获得某个表达式匹配的文本,这种功能被称为反向引用,通过\num来表示一个反向引用,num为分组的编号

断言

正则表达式中的大多数结构匹配的文本都会出现在最终的匹配结果中,但是有些结果只是用来判断某个位置左/右侧的文本是否符合要求

  • 单词边界\b表示希望匹配具体单词(由空格分隔)而非某个子字符串
  • 起始锚点^和结束锚点$,用来表示字符串的开始位置和结束位置

环视

有时候需要在某个位置向左或者向右看,要求必须出现或者不能出现某些字符,这种功能被称为向前/向后断言

  • (?!)就是“向后否定断言”,这个子表达式表示当前位置之后,不允许存在这个子表达式能够匹配的字符,从而限定这个匹配前面的字符或分组
  • (?=)就是“向后肯定断言”,用于限定当前位置之后必须这个匹配指定的子表达式

在过去的JavaScript中,并不支持向前断言,所幸的是在ES2018中,已经开始支持向前断言了,详情可以阅读下面的ES2018新增的正则特性

模式匹配

匹配模式是指匹配时使用的规则,设定特定的规则,可能会改变对正则表达式的识别

  • 不区分大小写模式对应的模式修饰符是i
  • 单行模式对应的模式修饰符是s,主要用来改变.的匹配规则
  • 单行模式对应的模式修饰符是m,多行模式影响的是^和$的匹配规则

ES2018新增的正则特性

参考

支持后行断言

在此之前JavaScript只支持先行断言,即向后匹配

  • (?=xxx),匹配后面包含xxx字符串
  • (?!xxx),匹配后面不包含xxx字符串

ES2018中增加了后行判断,即向前匹配

  • (?<=xxx),匹配前面包含xxx字符串
  • (?<!xxx),匹配前面不包含xxx字符串

在之前的版本会报错

Invalid regular expression: /(?<=€)\d+(.\d*)?/: Invalid group

// 匹配前面包含两个数字,且数字不为14的 ` cases`字符串
const re = /(?<=\d{2})(?<!14) cases/;
console.log(re.exec("20 cases")); // [" cases", index: 2, input: "20 cases", groups: undefined]
console.log(re.exec("14 cases")); // null

捕获命名

之前只能通过数字标获取分组的引用,现在支持命名和按名称引用

  • (?<name>)可以给捕获分组进行命名了,
  • \k<name>获取之前捕获的命名分组
js
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
let match = re.exec("2020-03-04");

// 输出结果为
// {
//     0: "2020-03-04"
//     1: "2020"
//     2: "03"
//     3: "04"
//     groups:
//          day: "04"
//          month: "03"
//          year: "2020"
//     index: 0
//     input: "2020-03-04"
// }

新增模式\s

在该模式下 .可以匹配包括换行符的任何字符了,等价于之前的[\w\W]

Unicode匹配模式增强

u 修饰符可以识别所有大于 0xFFFF 的 Unicode 字符。

  • \p{Number} 匹配所有数字,包括㉛㉜㉝ⅠⅡⅢⅣ
  • \p{Alphabetic}可以匹配所有Alphabetic,包括汉字、字母等