• ES6 入门教程 6 正则的扩展 6.10 Unicode 属性类 & 6.11 v 修饰符:Unicode 属性类的运算 & 6.12 具名组匹配


    ES6 入门教程

    ECMAScript 6 入门

    作者:阮一峰

    本文仅用于学习记录,不存在任何商业用途,如侵删

    6 正则的扩展

    6.10 Unicode 属性类

    ES2018 引入了 Unicode 属性类,允许使用\p{...}\P{...}\P\p的否定形式)代表一类 Unicode 字符,匹配满足条件的所有字符。

    const regexGreekSymbol = /\p{Script=Greek}/u;
    regexGreekSymbol.test('π') // true
    
    • 1
    • 2

    在这里插入图片描述

    上面代码中,\p{Script=Greek}表示匹配一个希腊文字母,所以匹配π成功。

    Unicode 属性类的标准形式,需要同时指定属性名和属性值。

    \p{UnicodePropertyName=UnicodePropertyValue}
    
    • 1

    但是,对于某些属性,可以只写属性名,或者只写属性值。

    \p{UnicodePropertyName}
    \p{UnicodePropertyValue}
    
    • 1
    • 2

    \P{…}\p{…}的反向匹配,即匹配不满足条件的字符。

    注意,这两种类只对 Unicode 有效,所以使用的时候一定要加上u修饰符。如果不加u修饰符,正则表达式使用\p\P会报错。

    由于 Unicode 的各种属性非常多,所以这种新的类的表达能力非常强。

    const regex = /^\p{Decimal_Number}+$/u;
    regex.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼') // true
    
    • 1
    • 2

    在这里插入图片描述

    上面代码中,属性类指定匹配所有十进制字符,可以看到各种字型的十进制字符都会匹配成功。

    \p{Number}甚至能匹配罗马数字。

    // 匹配所有数字
    const regex = /^\p{Number}+$/u;
    regex.test('²³¹¼½¾') // true
    regex.test('㉛㉜㉝') // true
    regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    下面是其他一些例子。

    // 匹配所有空格
    \p{White_Space}
    
    // 匹配各种文字的所有字母,等同于 Unicode 版的 \w
    [\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
    
    // 匹配各种文字的所有非字母的字符,等同于 Unicode 版的 \W
    [^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
    
    // 匹配 Emoji
    /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu
    
    // 匹配所有的箭头字符
    const regexArrows = /^\p{Block=Arrows}+$/u;
    regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    6.11 v 修饰符:Unicode 属性类的运算

    有时,需要向某个 Unicode 属性类添加或减少字符,即需要对属性类进行运算。现在有一个提案,增加了 Unicode 属性类的运算功能。

    它提供两种形式的运算,一种是差集运算(A 集合减去 B 集合),另一种是交集运算。

    // 差集运算(A 减去 B)
    [A--B]
    
    // 交集运算(A 与 B 的交集)
    [A&&B]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上面两种写法中,A 和 B 要么是字符类(例如[a-z]),要么是 Unicode 属性类(例如\p{ASCII})。

    而且,这种运算支持方括号之中嵌入方括号,即方括号的嵌套。

    // 方括号嵌套的例子
    [A--[0-9]]
    
    • 1
    • 2

    这种运算的前提是,正则表达式必须使用新引入的v修饰符。前面说过,Unicode 属性类必须搭配u修饰符使用,这个v修饰符等于代替u,使用了它就不必再写u了。

    下面是一些例子。

    // 十进制字符去除 ASCII 码的0到9
    [\p{Decimal_Number}--[0-9]]
    
    // Emoji 字符去除 ASCII 码字符
    [\p{Emoji}--\p{ASCII}]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    6.12 具名组匹配
    6.12.1 简介

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

    const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
    
    • 1

    上面代码中,正则表达式里面有三组圆括号。

    使用exec方法,就可以将这三组匹配结果提取出来。

    const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
    
    const matchObj = RE_DATE.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

    在这里插入图片描述

    组匹配的一个问题是,每一组的匹配含义不容易看出来,而且只能用数字序号(比如matchObj[1])引用,要是组的顺序变了,引用的时候就必须修改序号。

    ES2018 引入了具名组匹配(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。

    const RE_DATE = /(?\d{4})-(?\d{2})-(?\d{2})/;
    
    const matchObj = RE_DATE.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
    • 4
    • 5
    • 6

    在这里插入图片描述

    上面代码中,“具名组匹配”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?),然后就可以在exec方法返回结果的groups属性上引用该组名。同时,数字序号(matchObj[1])依然有效。

    具名组匹配等于为每一组匹配加上了 ID,便于描述匹配的目的。如果组的顺序变了,也不用改变匹配后的处理代码。

    如果具名组没有匹配,那么对应的groups对象属性会是undefined

    const RE_OPT_A = /^(?a+)?$/;
    const matchObj = RE_OPT_A.exec('');
    
    matchObj.groups.as // undefined
    'as' in matchObj.groups // true
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    上面代码中,具名组as没有找到匹配,那么matchObj.groups.as属性值就是undefined,并且as这个键名在groups是始终存在的。

    6.12.2 解构赋值和替换

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

    let {groups: {one, two}} = /^(?.*):(?.*)$/u.exec('foo:bar');
    one  // foo
    two  // bar
    
    • 1
    • 2
    • 3

    字符串替换时,使用$<组名>引用具名组。

    let re = /(?\d{4})-(?\d{2})-(?\d{2})/u;
    
    '2015-01-02'.replace(re, '$/$/$')
    // '02/01/2015'
    
    • 1
    • 2
    • 3
    • 4

    上面代码中,replace方法的第二个参数是一个字符串,而不是正则表达式。

    replace方法的第二个参数也可以是函数,该函数的参数序列如下。

    '2015-01-02'.replace(re, (
       matched, // 整个匹配结果 2015-01-02
       capture1, // 第一个组匹配 2015
       capture2, // 第二个组匹配 01
       capture3, // 第三个组匹配 02
       position, // 匹配开始的位置 0
       S, // 原字符串 2015-01-02
       groups // 具名组构成的一个对象 {year, month, day}
     ) => {
     let {day, month, year} = groups;
     return `${day}/${month}/${year}`;
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    具名组匹配在原来的基础上,新增了最后一个函数参数:具名组构成的一个对象。函数内部可以直接对这个对象进行解构赋值。

    6.12.3 引用

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

    const RE_TWICE = /^(?[a-z]+)!\k$/;
    RE_TWICE.test('abc!abc') // true
    RE_TWICE.test('abc!ab') // false
    
    • 1
    • 2
    • 3

    数字引用(\1)依然有效。

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

    这两种引用语法还可以同时使用。

    const RE_TWICE = /^(?[a-z]+)!\k!\1$/;
    RE_TWICE.test('abc!abc!abc') // true
    RE_TWICE.test('abc!abc!ab') // false
    
    • 1
    • 2
    • 3
  • 相关阅读:
    RS485协议和Modbus协议有什么区别?工业网关能用吗?
    数据库迁移-国产化-Oracle迁移至GBase8a(存储过程)
    cdn与云服务器有什么区别
    字符串拼接,字符串分割,字符串转集合,把Map转String,将字符串转报文
    hadoop2-hive
    浅谈本地缓存的几种方案选型
    GPU 虚拟化技术MIG简介和安装使用教程
    Docker - compose常用命令(常规操作顺序)
    延宕执行,妙用无穷,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中defer关键字延迟调用机制使用EP17
    Python 读取PostgreSQL的geometry字段时,获取geometry的中心点
  • 原文地址:https://blog.csdn.net/weixin_44226181/article/details/127858985