文章目录
RegExp 构造函数
- 在ES5中,
RegExp
构造函数的参数有两种情况:
- 第一个参数是字符串,第二个参数表示正则表达式的修饰符(flag)
var regex = new RegExp('xyz','i');
// 等价于
var regex = /xyz/i;
- 参数是一个正则表达式。返回一个原有正则表达式的拷贝。
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
.
两个解决办法:
- 变通的写法
console.log(/foo[^]bar/.test('foor\nbar')); // true
- 使用
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 入门》所做的笔记,大部分例子来源于此书。