第 5 章 正则的扩展

正则的扩展

1. RegExp 构造函数

  • 在 ES5 中,RegExp 构造函数的参数有两种情况
    1. 第一种情况是,第一个参数是字符串,第二个参数表示正则表达式的修饰符(flag)
      1
      var regex = new RegExp("xyz", "i");
  1. 第二种情况是,参数是一个正则表达式,则不允许使用第二个参数
    1
    var regex = new RegExp(/xyz/i);
  • ES6 的 RegExp 构造函数第一个参数是正则表达式,可以使用第二个参数指定修饰符。而且返回的正则表达式会忽略原有正则表达式的修饰符,只使用新指定的修饰符
    1
    let regex = new RegExp(/xyz/ig, "i");

2. 字符串的正则方法

  • 字符串对象共有 4 个方法可以使用正则表达式:match()、replace()、search() 和 split()

3. u 修饰符

  • ES6 对正则表达式添加了 u 修饰符,含义为”Unicode 模式“,用来正确处理大于 \uFFFF 的 Unicode 字符

4. RegExp.prototype.unicode 属性

  • 正则实例对象新增 unicode 属性,表示是否设置了 u 修饰符

5. y 修饰符

  • ES6 还为正则表达式添加了 y 修饰符,叫作”粘连“修饰符
  • y 修饰符的作用与 g 修饰符类似,也是全局匹配,后一次匹配都是从上一次匹配成功的下一个位置开始。不同之处在于,g 修饰符只要剩余位置中存在匹配就行,而 y 修饰符会确保匹配必须从剩余的第一个位置开始,这也就是”粘连“的涵义

6. RegExp.prototype.sticky 属性

  • 与 y 修饰符相匹配,ES6 的正则对象多了 sticky 属性,表示是否设置了 y 修饰符

7. RegExp.prototype.flags 属性

  • ES6 为正则表达式新增了 flags 属性,会返回正则表达式的修饰符
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // ES5 的 source 属性
    // 返回正则表达式的征文
    /abc/ig.source
    // "abc"

    // ES6 的 flags 属性
    // 返回正则表达式的修饰符
    /abc/ig.flags
    // "gi"

8. s 修饰符:dotAll 模式

  • 正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,但是行终止符除外
  • 提案:引入 s 修饰符,使得 . 可以匹配任意单个字符。这被称为 dotAll 模式,即点代表一切字符。所以正则表达式还引入了一个 dotAll 属性,返回一个布尔值,表示该正则表达式是否处在 dotAll 模式下

9. 后行断言

  • JavaScript 的正则表达式只支持先行断言和先行否定断言,不支持后行断言
  • 提案:引入后行断言,其中 V8 引擎4.9版本已经支持
  • 先行断言:x 只有在 y 前面才匹配,必须写成 /x(?=y)/ 的形式
  • 先行否定断言:x 只有不在 y 前面才匹配,必须写成 /x(?!y)/ 的形式
  • 后行断言:x 只有在 y 后面才匹配,必须写成 /(?<=y)x/ 的形式

10. Unicode 属性类

  • 提案:引入一种新的类的写法:\p{…} 和 \P{…},允许正则表达式匹配符合 Unicode 某种属性的所有字符
  • 这两种类都只对 Unicode 有效,所以使用的时候一定要加上 u 修饰符

11. 具名组匹配

简介

  • 正则表达式使用圆括号进行组匹配

    1
    2
    3
    4
    5
    6
    7
    8
    const RE_DATA = /(\d{4})-(\d{2})-(\d{2})/;

    // 使用 exec 方法可以将 3 组匹配结果提取出来

    const matchObj = RE_DATA.exec("1999-12-31");
    const year = matchObj[1]; // 1999
    const month = matchObj[2]; // 12
    const day = matchObj[3]; // 31
  • 组匹配的问题,每一组匹配的含义看不出来,而且只能使用数字序号引用

  • 提案:具名组匹配,允许为每一个组指定一个名字
    1
    2
    3
    4
    5
    6
    7
    8
    const RE_DATA = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

    // 使用 exec 方法可以将 3 组匹配结果提取出来

    const matchObj = RE_DATA.exec("1999-12-31");
    const year = matchObj.groups.year; // 1999
    const month = matchObj.groups.month; // 12
    const day = matchObj.groups.day; // 31

解构赋值和替换

  • 有了具名组匹配,可以使用解构赋值直接从匹配结果上为变量赋值

    1
    2
    3
    let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec("foo: bar");
    one // foo
    two // bar
  • 字符串替换时,使用 $<组名> 引用具名组

    1
    2
    3
    4
    let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;

    "2015-01-02".replace(re, "$<day>/$<month>/$<year>");
    // "02/01/2015"

引用

  • 如果要在正则表达式内部引用某个”具名组匹配“,可以使用 \k<组名> 的写法

    1
    2
    3
    const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
    RE_TWICE.test("abc!abc") // true
    RE_TWICE.test("abc!ab") // false
  • 数字引用依然有效

    1
    2
    3
    const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
    RE_TWICE.test("abc!abc") // true
    RE_TWICE.test("abc!ab") // false

12. String.prototype.matchAll

  • 如果一个正则表达式在字符串里面有多个匹配,现在一般使用 g 修饰符或 y 修饰符,在循环里面逐一取出
  • 提案:增加 String.prototype.matchAll 方法,可以一次性取出所有匹配,返回的是一个遍历器(Iterator),而不是数组
-------------本文结束 感谢您的阅读-------------
您的支持将鼓励我继续创作!