RegExp in Node.js

echosoar 原创发表于 2019/11/13 15:35:33
#node #regexp #正则表达式 #断言
虽说正则表达式这个东西通常是“write only”的,就算是自己写的过两天再看也会完全懵逼,记得有个笑话是这么说的:“假设你有一个问题,你决定用正则表达式来解决它,那么现在你有两个问题”,虽然有无数的缺点,但是当你掌握了正则表达式之后,在处理一个很复杂的处理的时候,代码量极小,写起来真快。
本篇文章不是正则的基本用法文档,而是来介绍一些稍微特别一点的东西。

断言

有时候我们从人群说一个人的位置,通常会先找一个参照物,比如说长的最高的人前面的那个小白脸。断言的意思用大白话来说呢就是当你寻找一个字符的时候,看看它的前后字符是不是和预期的一致。
在js中正则的断言都是零宽的,零宽的意思呢就是说无论有没有匹配到,断言是不进行捕获的,同时也不消耗字符。
断言包含正向(肯定 (?=pattern) 、否定(?!pattern))、反向(肯定(?<=pattern)、否定(?<!pattern))四种。正反方向是按照阅读的顺序来看的,一个字符的后面就是它的正向;肯定与否定的意思就是说是不是可以匹配某些字符。
so easy,看几个例子就明白了。

正向肯定断言

const reg_la_positive = /ab(?=cd)/;
reg_la_positive.exec('abcd');
其结果为 [ 'ab' ],并不会捕获后面的cd,同时对于 abc 这样的字符串,并不会匹配成功。

正向否定断言

const reg_la_negative = /ab(?!cd)/;
reg_la_negative.exec('abba');
其结果为 [ 'ab' ],并不会捕获后面的ba,同时对于 abcd 这样的字符串,并不会匹配成功。

反向肯定断言:

const reg_lb_positive = /(?<=ab)cd/;
reg_lb_positive.exec('abcd');
其结果为 [ 'cd' ],并不会捕获前面的ab,同时对于 bcd 这样的字符串,并不会匹配成功。

反向否定断言:

const reg_lb_negative = /(?<!ab)cd/;
reg_lb_negative.exec('dccd');
其结果为 [ 'cd' ],并不会捕获前面的dc,同时对于 abcd 这样的字符串,并不会匹配成功。

flags

对于 igm大家都可能比较熟悉,分别代表不区分大小写匹配、全局匹配和跨多行匹配,那么接下来就来说点很少用的flag。

Dot matches all - s

一般来说我们使用 . 来匹配任意字符,但是在js中的.并不能匹配换行、回车等符号,通过s标识符就能让正则识别换行、回车、行分隔符和段落分隔符。

Unicode - u

在原本的js正则表达式中,/\u{1024}/ 这样的表达式会被看做与 /u{1024}一致,其意义是匹配连续1024个u。 在ES6中新增的u flag支持在匹配中使用Unicode字符转义。
/\u{61}/u.test('a') // true
/\u212A/iu.test('K')  // true
/\u212A/iu.test('k')  // true
/\u017F/iu.test('S')  // true
/\u017F/iu.test('s')  // true
同时使用了u flag之后,原本的例如 /\a/u 这样的转义表达式就不再与 /a/u 等同了。所有的未被使用的转义字符将被作为保留字符,比如\p 等等,使用了这些保留的字符会直接报错。

Sticky - y

简单的说,y flag的作用就是再某个位置精确的匹配,其实就算简单的说也很难明白它到底是干嘛的,那就用下面的例子来说明一下。
在全局匹配的正则表达式中,存在lastIndex属性,用来标识下一次匹配从什么地方开始:
let str = 'const siteName = "iamgy"';
let regexp = /\w+/g;
let result;
while (result = regexp.exec(str)) {
  console.log( `match ${result[0]}, next index is ${regexp.lastIndex}` );
  // match const, next index is 5
  // match siteName, next index is 14
  // match iamgy, next index is 23
}
同时可以指定 lastIndex ,让搜索直接从某个位置开始,比如:
let str = 'const siteName = "iamgy"';
let regexp = /\w+/g;
regexp.lastIndex = 5;
let result;
while (result = regexp.exec(str)) {
  console.log( `match ${result[0]}, next index is ${regexp.lastIndex}` );
  // match siteName, next index is 14
  // match iamgy, next index is 23
}
不知道有没有发现一个问题,index 5的位置是一个空格,而全局匹配会继续向后面去搜索,这样的话如果我们要匹配某个index位置的字符时就无法处理。
y flag的作用就是精确匹配某一个位置的字符是不是符合:
const str = '0123a';
const regG = /a/g;
regG.lastIndex = 3;
const regY = /a/y;
regY.lastIndex = 3;

console.log(regG.exec(str));
// ["a", index: 4, input: "0123a", groups: undefined]

console.log(regY.exec(str));
// null