ECMAScript6--正则的扩展

蓝咒 提交于 2019-12-20 01:38:02

RegExp构造函数

在ES5中,RegExp构造函数的参数有两种情况

第一种: 参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)。

var regex = new RegExp('xyz', 'i');
// 等价于
var regex = /xyz/i;

第二种: 参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。

但是不允许使用第二个参数添加修饰符,否则会报错

var regex = new RegExp(/xyz/i);
// 等价于
var regex = /xyz/i;

ES6中改变了这种行为,如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符,而且返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符.

new RegExp(/abc/ig, 'i').flags
// "i"

//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]

字符串的正则方法

字符串对象共有4个方法,可以使用正则表达式:

  • match()
  • replace()
  • search()
  • split()

ES6将这4个方法,在语言内部全部调用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修饰符,含义为"Unicode 模式",用来正确处理大于\uFFFF的Unicode 字符(即会正确处理四个字节的 UTF-16 编码)

/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true

加了u修饰符后,ES6会将 \uD83D\uDC2A 识别为一个字符,所以第一行代码为false

总结:要想识别码点大于oxFFFF的Unicode字符,就需要加上u修饰符

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

  1. 点字符

点( . )字符在正则表达式中,含义是除了换行符以外的任意单个字符,对于码点大于oxFFFF的Unicode字符,点字符不能识别,必须加上u修饰符

var s = '𠮷';

/^.$/.test(s) // false
/^.$/u.test(s) // true

2.Unicode字符表示法

ES6 新增了使用大括号表示 Unicode 字符,这种表示法在正则表达式中必须加上 u 修饰符,才能识别当中的大括号,否则会被解读为量词。

//不加u修饰符,正则表达式会认为匹配61个连续的u
/\u{61}/.test('a') // false

/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true

3.量词

使用u修饰符后,所有量词都会正确识别码点大于0xFFFF的Unicode字符.

/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true

4.预定义模式

u修饰符也影响到预定义模式(空白字符类), 加了u修饰符,才能正确识别码点大于oxFFFF的Unicode字符

5.i修饰符

img

RegExp.prototype.unicode 属性

检查一个正则表达式是否设置了u修饰符

设置了返回true,否则返回false

y修饰符

ES6添加了y修饰符,叫做"粘连(sticky)"修饰符

y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。但是y修饰符确保匹配必须从剩余的第一个位置开始(这也就是"粘连"的涵义)

RegExp.prototype.sticky属性

检测正则表达式是否设置了y修饰符

设置了返回true,否则返回false

RegExp.prototype.flags属性

返回正则表达式的修饰符

s修饰符:dotAll模式

( . )字符不能匹配的两种情况:

  1. 四个字节的UTF-16字符
  2. 行终止符(表示一行的终结)
  • U+000A 换行符(\n)
  • U+000D 回车符(\r)
  • U+2028 行分隔符(line separator)
  • U+2029 段分隔符(paragraph separator)

加了s修饰符后,点( . )可以代表任意单个字符

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

使用dotAll属性检查一个正则表达式是否设置了s修饰符

设置了返回true,否则返回false

Unicode属性类

新的类的写法:

  • 大写的P : \P{...}
  • 小写的p : \p{...}

以上两种写法允许正则表达式匹配符合Unicode某种属性的所有字符

//Unicode 属性类要指定属性名和属性值。
\p{UnicodePropertyName=UnicodePropertyValue};

//对于某些属性,可以只写属性名,或者只写属性值。
\p{UnicodePropertyName}
\p{UnicodePropertyValue}

注意:

  • 大写P的写法是小写p的反向匹配,即匹配不满足条件的字符
  • 这两种类(写法)只对Unicode有效,所以使用的时候一定要加上u修饰符,不然会报错

具名组匹配

原先获取分组内容:

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

分组只能通过数字序号引用来获取,一旦组的顺序变了,引用的时候就必须修改序号
具名组匹配: 为每个分组指定一个名字
用法: 模式的头部添加“问号 + 尖括号 + 组名” (?<year>);
获取: 匹配结果的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

使用解构赋值和替换

使用解构赋值直接从匹配结果上为变量赋值。

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} = groups;
 return `${day}/${month}/${year}`;
});

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

引用

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

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

数字引用依然有效(可以同时使用)

//数字引用
const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false

//混合使用
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/;
RE_TWICE.test('abc!abc!abc') // true
RE_TWICE.test('abc!abc!ab') // false

String.prototype.matchAll

用法: 字符串.matchAll(正则表达式)
用于一次性取出所有匹配,返回一个遍历器

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"]

遍历器转换为数组:
使用…运算符和Array.from()

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!