在中我们讲了正则表达式的基本用法,接下来博主想聊聊其中的细节,今天就从正则修饰符开始吧。
正则修饰符又称为正则标记 (flags),它会对正则的匹配规则做限定,进而影响匹配的最终结果。在上次的文章中我们也提到过,正则修饰符一共有以下几种,可以单独使用,也可以组合使用:
- /\w+/g; // global search
- /\w+/i; // ignore case
- /\w+/m; // multi-line
- /\w+/u; // unicode
- /\w+/y; // sticky
- /\w+/gi;
- new RegExp('\\w+', 'gi');
其中的 i 好理解,正如上面的注释一样,ignore case 或 case insensitive,忽略大小写。
下面是一个简单的例子,正则表达式加上了 i 修饰符之后也可以匹配到大写字母:
- 'Hello World'.match(/hello/i); // ["Hello"]
- / hello / i.exec('Hello World'); // ["Hello"]
再来看看全局匹配修饰符 g,下面是一个全局匹配的例子:
- var source = 'hello world hello JS';
- source.match(/hello/); // ["hello"]
- source.match(/hello/g); // ["hello", "hello"]
从上面代码中可以看出,普通正则的匹配结果只有一个,如果想要找出全部的匹配结果,后面则需要加一个 g 修饰符,使其成为全局匹配模式。
全局修饰符 g 通常也会和多行匹配修饰符 m 结合使用,我们将上面例子稍加改动,添加一个换行符,正则也稍加修改:
- var source = 'hello world\nhello JS';
- source.match(/^hello.+/g); // ["hello world"]
大家会看到,我们是要在多行文本中匹配以 "hello" 开头的字符串,但结果只出现了第一个匹配项,后面的 "hello JS" 并未匹配到,这时我们需要加入多行匹配修饰符 m:
- var source = 'hello world\nhello JS';
- source.match(/^hello.+/gm); // ["hello world", "hello JS"]
现在,所有的结果都匹配到了。
但需要注意的是,单独使用修饰符 m 是不起作用的,它必须和 g 相结合,就像下面例子一样,虽然有 m 修饰符,但仍旧只匹配到了第一行文字:
- var source = 'hello world\nhello JS';
- source.match(/^hello.+/m); // ["hello world"]
另外,还有一个很重要的条件,那就是,只有正则中包含起始标记 "^" 或结束标记 "$" 时,修饰符 m 才会发挥它的作用,否则 g 不需要 m,且看下面例子:
- // 只有匹配开始标记^或结束标记$时,g才需要m
- var source = 'hello world\nhey world';
- // 正则中没有^或$ 只需g即可匹配多行
- source.match(/he.+/g); // ["hello world", "hey world"]
- // 正则中含有^或$ g只能匹配第一个结果
- source.match(/^he.+/g); // ["hello world"]
- source.match(/.+world$/g); // ["hey world"]
- // 含有^或$的情况下 需要添加m 才可以匹配多行
- source.match(/^he.+/gm); // ["hello world", "hey world"]
- source.match(/.+world$/gm); // ["hello world", "hey world"]
以上介绍的都是正则修饰符在 String#match() 方法中的表现,我们也知道,RegExp#exec() 是与之对应的一个方法,同样可以匹配字符串,返回结果数组,那么这个 exec() 方法对于含有全局修饰符的正则又会有什么样的表现呢?实际操作发现,RegExp#exec() 方法与上面 String#match() 的规则大致相同,但不同的是,RegExp#exec() 方法每次只会匹配一个结果,所以需多次环执行才能获取全部。我们来看下面示例:
- var regex = /^hello.+/gm;
- var source = 'hello world\nhello JS';
- regex.exec(source); // ["hello world"]
- regex.exec(source); // ["hello JS"]
可以看到每一次执行正则实例的 exec() 方法都会返回一个结果数组,由于正则中含有起始标记 ^ 和 gm 组合,我们需要执行两次才能获取到全部的结果,这是与 String#match() 方法不同的地方。一般来说,我们可以使用循环结构调用 RegExp#exec() 方法来获取所有的结果:
- var result = null;
- while (result = regex.exec(source)) {
- console.log(result);
- }
- // output:
- // ["hello world"]
- // ["hello JS"]
对于 RegExp#test() 方法,一般是用来检测字符串是否匹配某种模式,如果要在多行中检测任意一行是否匹配时,同样需要 gm 组合,下面代码先简单检测匹配情况,然后在多行中进行匹配:
- var source = 'hello world\nhey JS';
- /^hello.+/.test(source); // true
- /^hey.+/.test(source); // false
- /^hey.+/g.test(source); // false
- /^hey.+/gm.test(source); // true
从结果来看,不加 gm 修饰符的正则,只能检测一行数据的匹配情况,加入 gm 后可以对多行进行检测,只要任意一行符合条件,即返回 true。
最后再来说说 String#replace() 方法,同样地,如果正则中出现了 ^ 或 $,那就需要加上 gm 组合,下面代码演示了多行替换的操作:
- var source = 'hello world\nhello JS';
- // 正则中没有^或$,全局g轻松搞定
- source.replace(/hello/g, 'hey'); // "hey world\nhey JS"
- // 正则中含有^或$,全局g也无能为力,仅能替换第一行
- source.replace(/^hello/g, 'hey'); // "hey world\nhello JS"
- // 需要使用gm组合
- source.replace(/^hello/gm, 'hey'); // "hey world\nhey JS"
上面是全局匹配 g 和多行匹配 m,下面介绍一下 u 修饰符。
u 修饰符是 ES6 新增特性,可以启用 Unicode 模式对字符串进行正则匹配,能正确处理四个字节的 UTF-16 字符集。为什么需要这个修饰符呢,我们先来看一个例子:
- /^.{3}$/.test('你好啊'); // true
- / ^. {
- 3
- }
- $ / .test(''
来源: