ES6学习笔记4 正则表达式的扩展

正则表达式的基础知识:https://blog.csdn.net/zjw_python/article/details/80845847

RegExp构造函数

ES6允许在构造函数第一个参数为正则表达式的情况下,使用第二个参数添加修饰符,而在ES5中会报错

var regex = new RegExp(/abc/ig, 'i');

若传入的正则表达式已有修饰符,则会被第二个参数覆盖


字符串正则方法

match()replace()search()split()方法全部定义到RegExp对象上,从而在语言内部全部调用RegExp的实例方法。

  • String.prototype.match 调用 RegExp.prototype[Symbol.match]
  • String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
  • String.prototype.search 调用 RegExp.prototype[Symbol.search]
  • String.prototype.split 调用 RegExp.prototype[Symbol.split]

u修饰符

ES6对正则表达式添加了u修饰符,用来正确处理大于\uFFFF的 Unicode 字符,因此其可以正确处理四个字节的UTF-16编码

/^\uD83D/u.test('\uD83D\uDC2A');  //false  能正确识别4字节的字符
/^\uD83D/.test('\uD83D\uDC2A'); // true  错误识别为两个字符

除此之外,一旦加上u修饰符号,就会修改下面这些正则表达式的行为:

  • 点字符.加上u修饰符后,就可以识别码点大于0xFFFF的 Unicode 字符
    这里写图片描述
  • ES6 新增了使用大括号表示 Unicode 字符,这种表示法在正则表达式中必须加上u修饰符,才能识别当中的大括号
/u{61}/.test('a');  //false
/u{61}/u.test('a');  //true
  • 使用u修饰符后,所有量词都会正确识别码点大于0xFFFF的 Unicode 字符
    这里写图片描述
  • u修饰符使预定义模式也能正确识别码点大于0xFFFF的 Unicode 字符
    这里写图片描述
  • 加入u修饰符使i修饰符能正确识别字形相近的字符。下面代码中,不加u修饰符,就无法识别非规范的K字符
/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true

y修饰符

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

下面是g修饰符和y修饰符的区别

const REGEX = /a/g;

// 指定从2号位置(y)开始匹配
REGEX.lastIndex = 2;

// 匹配成功
const match = REGEX.exec('xaya');

// 在3号位置匹配成功
match.index // 3

// 下一次匹配从4号位开始
REGEX.lastIndex // 4

// 4号位开始匹配失败
REGEX.exec('xaya') // null

g修饰符的情况下,初始匹配位置是2号位,也就是y字符,与正则表达式的a字符不符,其自动继续向下搜索至3号位,匹配成功

const REGEX = /a/y;

// 指定从2号位置开始匹配
REGEX.lastIndex = 2;

// 不是粘连,匹配失败
REGEX.exec('xaya') // null

// 指定从3号位置开始匹配
REGEX.lastIndex = 3;

// 3号位置是粘连,匹配成功
const match = REGEX.exec('xaya');
match.index // 3
REGEX.lastIndex // 4

而在y字符情况下,2号位匹配不成功后,就不再向下搜索,终止了匹配

因此,y字符实际上隐含了头部匹配标志^,其设计的本意就是让匹配的标志^在全局匹配中都有效

在字符串的replace方法中使用y修饰符,一旦匹配失败,后续即使出现符合的子字符串,也不会被替换

const REGEX = /a/gy;
'aaxa'.replace(REGEX,'_');   //'--xa'

单单一个 y 修饰符对match方法,只能返回第一个匹配,必须与 g 修饰符联用,才能返回所有匹配

'a1a2a3'.match(/a\d/y);   // ["a1"]
'a1a2a3'.match(/a\d/gy);  // ["a1", "a2", "a3"]

split方法中使用y修饰符,原字符串必须以分隔符开头,这意味着只要匹配成功,第一个成员肯定是空字符串

'x##'.split(/#/y);    //没有找到匹配
//['x##']
'##x'.split(/#/y);   //找到两个匹配
//['','','x']

后续的分隔符只要紧跟前面的分隔符才能被识别

'#x#'.split(/#/y);  //找到一个匹配
//['','x#']

s修饰符

正则表达式中,点.可以匹配任意单个字符,但行终止符除外,例如换行符(\n)、回车符(\r)。加入s修饰符,可以使点.匹配任意单个字符,包括行终止符。

/foo.bar/s.test('foo/nbar');    //true

可以使用dotAll属性检测正则表达式是否处于以上的模式,返回一个布尔值

const re = /foo.bar/s;
re.dotAll  //true

sticky和flags属性

可以通过sticky属性检测是否设置了y修饰符

var r = /hello\d/y;
r.sticky  //true

可以通过flags属性,返回正则表达式的修饰符

/abc/ig.source   //返回正则表达式的正文
//"abc"

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

后行断言

ES5只支持先行断言和先行否定断言,ES6引入后行断言

例如只匹配在美元符号后面的数字,要写成/(?<=\$)\d+/(先行断言),而只匹配不在美元符号后面的数字,要写成/(?<!\$)\d+/

/(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill');   //["100"]
/(?<!\$)\d+/.exec('it’s is worth about €90');                  //["90"]

下面是使用后行断言进行字符串替换的例子,只有美元符号后面的foo字符串被替换

const re = /(?<=\$)foo/g;

'$foo %foo foo'.replace(re, 'bar');
// '$bar %foo foo'

后行断言的组匹配与正常情况也是不同的,原因在于后行断言是从右往左执行的

/(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"]
/^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"]

在正常情况下,第一个捕获组为贪婪模式,因此为”105”,而第二个捕获组为”3”。而在后行断言中,由于执行顺序相反,得到的第一个捕获组为”1”(实际上其是第二个被执行的),而第二个捕获组为”053”(实际其是第一个被执行的)。

因此后行断言中向后引用,应该写在捕获组的前面,否则无法引用捕获组

/(?<=(o)d\1)r/.exec('hodor')  // null
/(?<=\1d(o))r/.exec('hodor')  // ["r", "o"]

上述的捕获组(o)的引用\1就写在了捕获组前面


Unicode属性类

ES2018引入一种新的类的写法\p{...}\P{...},允许正则表达式匹配符合 Unicode 某种属性的所有字符。使用该类时,务必加上u修饰符,否则会报错

Unicode 属性类要指定属性名和属性值,格式为:

\p{UnicodePropertyName=UnicodePropertyValue}

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

\p{UnicodePropertyName}
\p{UnicodePropertyValue}

下述代码匹配一个希腊文字母

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

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

匹配各种数字

const regex = /^\p{Number}+$/u;
regex.test('²³¹¼½¾') // true
regex.test('㉛㉜㉝') // true
regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true

命名组匹配

ES6添加命名捕获组功能,可以在exec方法返回结果的groups属性上调用组名

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\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

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

const RE_OPT_A = /^(?<as>a+)?$/;
const matchObj = RE_OPT_A.exec('');

matchObj.groups.as // undefined
'as' in matchObj.groups // true

有了命名组匹配,就可以使用解构赋值的方式快速获取到对应捕获组的值

let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
one  // foo
two  // bar

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

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

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

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} = args[args.length - 1];
 return `${day}/${month}/${year}`;
});

在正则表达式内部引用某个命名捕获组,可以使用\k<组名>的写法,其可以与数字引用同时使用


String.prototype.matchAll

如果一个正则表达式在字符串里面有多个匹配,现在一般使用g修饰符或y修饰符,在循环里面逐一取出

var regex = /t(e)(st(\d?))/g;
var string = 'test1test2test3';

var matches = [];
var match;
while (match = regex.exec(string)) {
  matches.push(match);
}

matches
// [
//   ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"],
//   ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"],
//   ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]
// ]

目前有个提案,增加了String.prototype.matchAll方法,可以一次性取出所有匹配,其返回一个遍历器,可以使用for...of循环取出结果

const string = 'test1test2test3';

// g 修饰符加不加都可以
const regex = /t(e)(st(\d?))/g;

for (const match of string.matchAll(regex)) {
  console.log(match);
}
// ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
// ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
// ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]

也可以将其转换为数组

// 转为数组方法一
[...string.matchAll(regex)]

// 转为数组方法二
Array.from(string.matchAll(regex));

猜你喜欢

转载自blog.csdn.net/zjw_python/article/details/80853421
今日推荐