ES6学习笔记5:正则的扩展

RegExp 构造函数

  • 在ES5中,RegExp构造函数的参数有两种情况:
  1. 第一个参数是字符串,第二个参数表示正则表达式的修饰符(flag)
var regex = new RegExp('xyz','i');
// 等价于
var regex = /xyz/i;
  1. 参数是一个正则表达式。返回一个原有正则表达式的拷贝。
var regex = new RegExp(/xyz/i);
// 等价于
var regex = /xyz/i

对于第二种情况,ES5不允许添加修饰符,否则会报错。

var regex = new RegExp(/xyz/ig, 'i'); // 在ES5中报错
  • 在ES6中

如果RegExp构造函数的第一个参数是一个正则对象,第二个参数也是可以使用的,而且返回的是带有指定的修饰符的正则表达式。

字符串的正则方法

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

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

在ES6中,这四个方法在语言内部都调用了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编码)。

console.log(/^\uD83D/u.test('\uD83D\uDC2A')); // false ES6
console.log(/^\uD83D/.test('\uD83D\uDC2A')); // true ES5

在这里\uD83D\uDC2A是一个四字节的UTF-16的编码,代表一个字符,但是在ES5中,会将其识别为两个字符,从而导致结果为true.加了u修饰符以后,ES6就会识别为一个字符,从而导致结果为false.

需要注意:

加上u修饰符以后,会修改下面正则表达式的行为。

  • 点字符

点(.)的含义是:除了换行以外的任意单个字符,对于大于0xFFFF的Unicode字符,点字符不能识别,需要加上u修饰符

var s = '𠮷'; 

console.log(/^.$/.test(s)); // false
console.log(/^.$/u.test(s)); // true

在这里如果不加u修饰符,正则表达式会认识字符串是两个字符,从而匹配失败。

  • Unicode字符表示法

ES6新增了使用大括号表示Unicode字符,在正则表达式中,需要加上u修饰符才能识别其中的大括号,否则会被解读成量词。

console.log(/\u{61}/.test('a')); // false
console.log(/\u{61}/u.test('a')); // true
console.log(/\u{20BB7}/.test('𠮷';)); // true
  • 量词

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

console.log(/a{2}/.test('aa'));  // true
console.log(/a{2}/u.test('aa')); // true
console.log(/𠮷{2}/.test('𠮷𠮷')); // false
console.log(/𠮷{2}/u.test('𠮷𠮷')); // true
  • 预定义模式

对于一些内置的正则表达式。u修饰符也会影响到,使其正确的识别码点大于0xFFFF的Unicode字符

console.log(/^\s$/.test(𠮷)); // false
console.log(/^\s$/u.test(𠮷)); // true

可以利用这一点,可以写出一个正确返回字符串长度的函数

function codePointLength(text){
  var result = text.match(/[\s\S]/gu);
  return result ? result.length : 0;
}
  • i修饰符

有些Unicode 字符编码不同,但是自型很相似。比如:\u004B\u212A都是大写的K.加上u修饰符可以识别非规范的字符

console.log(/[a-z]/i.test('\u212A')); // false
console.log(/[a-z]/iu.test('\u212A')); // true
  • 转义

没有u修饰符的情况下,正则中没有定义的转义是无效的,但是在有u修饰符的情况下会报错

RegExp.prototype.unicode 属性检测是否设置了u修饰符

在ES6中,可以使用正则表达式的unicode属性,从而判断这个正则表达式是否设置了u修饰符。

const r1 = /hello/;
const r2 = /hello/u;
console.log(r1.unicode); // false 没有设置u修饰符
console.log(r2.unicode); // true 设置了u修饰符

y修饰符

在ES6中,添加了y修饰符(‘粘连’修饰符)。y修饰符是全局匹配后一次匹配都从上一次匹配成功的下一个位置开始,并且确保匹配必须从剩余的第一个位置开始(剩余字符串的头部开始)。y修饰符于g修饰符比较相似,但是g修饰符之要确保剩下的字符串含有匹配就可以了,而不需要从剩余字符串的头部开始。

var str = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;
// 第一次执行
r1.exec(s); // ['aaa'];
r2.exec(s); // ['aaa'];
// 第二次执行
r1.exec(s); // ['aa'];
r2.exec(s); // null;

上面代码中,由于第一个执行以后,剩余的字符串为:_aa_a是以_开头的,所以使用了y修饰符的r2匹配失败,返回null.

实际上,y修饰符隐含了头部匹配的标志^

RegExp.prototype.sticky 属性检测是否设置了y修饰符

在ES6中,可以使用正则表达式的sticky属性,从而判断这个正则表达式是否设置了y修饰符。

var r = /hello\d/y;
console.log(r.sticky); // true

##RegExp.prototype.flags 属性返回表达式修饰符

  • 在ES5中,可以使用正则表达式的```source``属性返回正则表达式的正文。
console.log(/abc/ig.source); // 'abc'
  • 在ES6中,可以使用正则表达式的flags属性获取正则表达式的修饰符信息
console.log(/abc/ig.flags); // 'gi'

s修饰符:dotAll模式

点(.)是一个特殊的字符,代表任意的单个字符,但是有两个例外:

  • 四个字节的UTF-16字符(使用u修饰符解决)
  • 终止符(换行符、回车符、行分隔符、段分隔符)
console.log(/foo.bar/.test('foor\nbar')); // false

由于.不匹配\n,所以正则表达式返回的是false.

两个解决办法:

  1. 变通的写法
console.log(/foo[^]bar/.test('foor\nbar')); // true
  1. 使用s修饰符,使得.可以匹配任意单个字符(ES2018引入)。
console.log(/foo.bar/s.test('foor\nbar')); // true

这种被称为是dotAll模式:点(dot)代表一切字符。所以正则表达式引入了dotAll属性,返回当前的正则表达式是否处于dotAll模式。

const re = new RegExp('foo.bar','s'); // 还可以写成 const re = /foo.bar/s/;

console.log(re.test('foor\nbar')); // true
console.log(re.dotAll); // true

使用/s修饰符和多行修饰符/m不冲突,两者一起使用的情况下,.匹配所有字符,^匹配每一行的行首,$匹配每一行的行尾。

后行断言

JavaScript 的正则表达式,只支持先行断言和先行否定断言,不支持后行断言和后行否定断言,ES2018引入后行断言,V8引擎4.9版(Chrome62)已经支持。

  • 先行断言:x只有在y前面才匹配,必须写成/x(?=y)/.
  • 先行否定断言:x只有不在y前面才匹配,必须写成/x(?!y)/
  • 后行断言:x只有在y的后面才匹配,必须写成/(?<=y)x.
  • 后行否定断言:x只有不在y后面才匹配,必须写成/(?<!y)x.

需要注意

  • 先行断言括号中的部分是不计入返回结果的
  • 后行断言括号中的部分是不计入返回结果的

Unicode 属性类

ES2018引入了\p{...}\P{...},允许正则表达式匹配符合Unicode某种属性的所有字符。

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

这里\p{Script=Greek}指定匹配希腊文字母,所以匹配成功。

Unicode 属性类要指定属性名和属性值

\p{UnicodePropertyName=UnicodePropertyValue}

对于一些属性可以只写属性名或者只写属性值。

  • \P{...}\p{...}的反向匹配,即:匹配不满足条件的字符。
  • 在使用的时候需要加上u修饰符,如果不加会报错。

具名组匹配

简介

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

const ReData = /(\d{4})-(\d{2})-(\d{2})/;
const matchObj = ReData.exec('1999-12-31');
console.log(matchObj); // ["1999-12-31", "1999", "12", "31", index: 0, input: "1999-12-31", groups: undefined]
console.log(matchObj[1]); // 1999
console.log(matchObj[2]); // 12
console.log(matchObj[3]); // 31

组匹配存在一个问题就是每组匹配的含义不容易看出来,而且只能用数字序号引用,如果组的顺序变了,引用的时候就要修改序号。

***ES2018***引入了具名组匹配,允许为每一组匹配指定一个名字,方便阅读代码和引用

具名组匹配:在圆括号内部,在匹配模式的头部添加“问号+尖括号+组名”(?<year>),然后使用exec()方法返回结果的groups属性上引用这个组名,并且数字序号依然有效。

如果具名组没有匹配,那么对应groups属性会是undefined,但是键名在groups中始终存在。

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

console.log(matchObj.groups.as); //undefined
console.log('as' in matchObj.groups) // true

解构赋值

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

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

替换

字符串替换的时候,可以使用$<groupName>引用具名组.

let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
console.log('2015-01-01'.replace(re, '$<day>/$<month>/$<year>')); // 01/01/2015

将匹配的字符取出来

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

const regex = /t(e)(st(\d?))/g;
const string = 'test1test2test3';
const matches = [];
let match;
while (match = regex.exec(string)) {
      console.log(match);
      matches.push(match);
    }

遍历器转换为数组:

  • 使用...运算符
[...Iterator]
  • 使用Array.from
Array.form(Iterator)

备注:本文是自己学习阮一峰老师的《ECMAScript 6 入门》所做的笔记,大部分例子来源于此书。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值