ES6(五) 正则的扩展
RegExp 构造函数
var regex = new RegExp('xyz', 'i');
// 等价于
var regex = /xyz/i;
var regex = new RegExp(/xyz/i);
// 注意 ES5 不允许在给定一个正则表达式后,
// 像上面一样继续添加修饰符,会报错
// ES6 则改变了这种行为,可以使用第二个参数来指定修饰符
// 返回的正则表达式会忽略原有的正则表达式的修饰符,指定参数所给的修饰符
// 等价于
var regex = /xyz/i;
参数既可以为字符串,也可以为一个正则表达式
字符串的正则方法
ES6 出现之前,字符串对象共有4个方法,可以使用正则表达式
- match()
- replace()
- search()
- split()
ES6将这4个方法,在语言内部全部调用 RegExp 的实例方法,从而做到所有与正则相关的方法,全都定义在 RegExp 对象中
- RegExp.prototype[Symbol.match]
- RegExp.prototype[Symbol.replace]
- RegExp.prototype[Symbol.search]
- RegExp.prototype[Symbol.split]
即将 String实例对象中过得有关正则方法全部内置到RegExp对象中。
u修饰符
ES6 对正则表达式添加了 u 修饰符,含义为 Unicode模式,用来正确处理大于 \uFFFF 的 Unicode 字符 意思就是会正确处理四个字节的 UTF-16 编码
/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true
// 详细参见字符串的扩展,字符串的unicode表示方式
常见的需要用到 u 修饰符的正则表达式
- 点字符
.
点字符在正则表达式中,含义是除了换行符以外的任意单个字符,对于码点大于 0xFFFF 的 Unicode 字符,点字符不能识别,必须加上 u 修饰符 - Unicode字符表示法
ES6 新增了使用大括号表示 Unicode字符,这种表示法在正则表达式中也必须加上 u 修饰符,才能够识别 否则会被解读为量词 - 量词
使用 u 修饰符后,所有量词都会正确识别码点大于 0xFFFF 的 Unicode 字符 - 预定义模式
\S
是预定义模式,匹配所有非空白字符,只有加了 u 修饰符,它才能正确匹配码点大于 0xFFFF 的 Unicode 字符 - 转义
没有u
修饰符的情况下,正则中没有定义的转义(如逗号的转义\,
)无效,而在u
模式会报错。
y 修饰符
粘连修饰符,y 修饰符的作用与 g 修饰符类似,也是全局匹配。后一次匹配都是从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始!并且第一次匹配也是从第一个位置开始
var s = '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 // 因为这里会从 _ 开始寻找,未找到返回null
var s = 'aaa_aa_a';
var r = /a+_/y;
r.exec(s) // ["aaa_"]
r.exec(s) // ["aa_"]
y
修饰符的设计本意,就是让头部匹配的标志^
在全局匹配中都有效。
g
修饰符会忽略非法字符,y
修饰符则不会
s 修饰符
正则表达式中,.
是一个特殊字符,代表任意的单个字符,但是有两个例外
-
四个字节的 UTF-16 字符,可以用 u 修饰符解决
-
行终止符
- U+000A 换行符(
\n
) - U+000D 回车符(
\r
) - U+2028 行分隔符(line separator)
- U+2029 段分隔符(paragraph separator)
ES2018 引入 s 修饰符,使得
.
可以匹配任意单个字符。这被称为 dotAll 模式,即
.
代表一切字符。所以,正则表达式还引入了一个 dotAll 属性 表示是否处于 dotAll 模式 - U+000A 换行符(
RegExp.prototype 上的属性及方法
RegExp.prototype.unicode
正则实例对象新增 unicode 属性,表示是否设置了 u 修饰符
RegExp.prototype.sticky
表示是否设置了y修饰符
RegExp.prototype.flags
返回设置的修饰符
String.prototype.matchAll()
如果一个正则表达式在字符串里面有多个匹配,现在一般使用g
修饰符或y
修饰符,在循环里面逐一取出。
ES2020 增加了String.prototype.mathchAll()方法,可以一次性取出所有匹配,不过它返回的是一个遍历器,而不是数组
相对于返回数组,返回遍历器的好处在于,如果匹配结果是一个很大的数组,那么遍历器比较节省资源。
遍历器转为数组是非常简单的,使用...
运算符和Array.from()
方法就可以了
// 转为数组的方法一
[...string.matchAll(regex)]
// 转为数组的方法二
Array.from(string.matchAll(regex))
后行断言
js语言的正则表达式,只支持先行断言和先行否定断言,不支持后行断言和后行否定断言,ES2018引入后行断言
Unicode 属性类
ES2018 引入了一种新的类的写法\p{...}
和\P{...}
,允许正则表达式匹配符合 Unicode 某种属性的所有字符。
const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π') // true
// 上面代码中
// \p{Script=Greek}指定匹配一个希腊文字母,所以匹配π成功。
Unicode 属性类要制定属性名和属性值
对于某些属性,可以只写属性名
\P{…}
是\p{…}
的反向匹配,即匹配不满足条件的字符。
注意,这两种类只对 Unicode 有效,所以使用的时候一定要加上
u
修饰符。如果不加u
修饰符,正则表达式使用\p
和\P
会报错,ECMAScript 预留了这两个类。
\p{Number}
甚至能匹配罗马数字。
具名组匹配
正则表达式使用圆括号进行组匹配。
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
// 组匹配的一个问题是,每一组的匹配含义不容易看出来,而且只能用数字序号(比如matchObj[1])引用,要是组的顺序变了,引用的时候就必须修改序号。
ES2018 引入了 具名组匹配,允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用
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"
“具名组匹配”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?<year>
),然后就可以在exec
方法返回结果的groups
属性上引用该组名。同时,数字序号(matchObj[1]
)依然有效。
如果具名组没有匹配,那么对应的groups
对象属性会是undefined
。
解构赋值和替换
-
有了具名组匹配以后,可以使用解构赋值直接从匹配结果上为变量赋值。
-
字符串替换时,使用
$<组名>
引用具名组。
引用
如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>
的写法。
总结
本文主要介绍了,ES6以来对正则表达式的扩展,主要有RegExp构造函数及其原型上的属性,字符串的正则方法,各种修饰符,以及居名组匹配等等。