js正则表达式扩展知识

创建正则

创建正则表达式有两种方式:字面量和实例

  • cont reg = /正则表达式/修饰符
  • const reg = new RegExp(正则表达式,修饰符)
const reg = /123123123aaa.*/g  || new RegExp('123123123aaa.*', 'g')

修饰符

i 忽略大小写匹配;m 多行匹配; g 全局匹配; 修饰符可以写多个,例如 /123aAbb/img

常用元字符

  • . 匹配出 \n 以外的任何字符;
  • \w匹配数字、字母、下划线;\W匹配非数字、字母、下划线;
  • \s 匹配空白字符(空格、Tab制表符、换行);\S 匹配非空白字符;
  • \d 匹配数字;\D 匹配非数字字符;
  • \n 换行符;^ 字符串的开始;$ 字符串的结束;
  • \b 匹配单词的开始或结束(开头、空格、-. 等都为单词的结束);\B 匹配非单词的开始或结束;
  • \ 或者;() 分组;[] 字符类;

数量元字符

  • * 重复 0-n 次;+ 重复 1-n 次; 重复 0-1 次;{x} 重复 x 次,x固定值;{x,y} 重复 x-y 次;

贪婪与惰性模式

  • 贪婪模式:如果符合要求就会一直往后匹配,一直到无法匹配 => 能多匹配就多匹配
  • 惰性模式:匹配到合适的内容后就终止匹配 => 能少匹配就少匹配

正则表达是默认为贪婪模式,如果想使用惰性模式,需要在数量元字符的后面加上

const val = '123456789abc';
console.log(/.*/.exec(val)[0]); // "123456789abc"
console.log(/.*?/).exec(val)[0]); // ""
console.log(/.+/).exec(val)[0]); // "123456789abc"
console.log(/.+?/).exec(val)[0]); // "1"
console.log(/\d{1,100}/.exec(val)[0]); // "123456789"
console.log(/\d{1,100}?/.exec(val)[0])); // "1"

\ 转译字符

如果要匹配具有特殊意义的特殊符号或者使用某些元字符,需要使用\ 来转义区分

  • \* 匹配字符 “*” (单独的* 代表* 前的字符重复 0-n 次 )
  • \. 匹配字符 “.” (单独的. 匹配\n 以外的任何字符 )
  • \w 匹配数字、字母、下划线 (单独的w 匹配字母w
  • ……

字符类

[] 字符类:字符类用于匹配某一范围的字符;[] 中匹配到的内容只能是一个字符

比如要查找数字,我们可以用\d ,也可以用字符类[0-9]一样可以生效,-代表着取值选择一个取值范围,如果想要匹配字符串- ,要加转义符[\-]

[0-15] 匹配的是0-15,并不是0、1、2、3、4...15

[aaaassddasdasddsda][asd] 匹配的都是asd

字符类中,^ 代表着非,是指匹配非 [^内容]中的内容

const reg = /a[dp]c/;  // 匹配 adc 或 apc
const reg = /[a-zA-Z0-9_]/; // 匹配数字、字母、下划线
const reg = /[^a-zA-Z0-9_]/; // 匹配非数字、字母、下划线

分组

() 可以进行内容的范围选择、捕获特定的内容等。

(123|132|321|312) 表示不论是123、132、321、312 都可以匹配;(123)? 这时的 是对123 这一组数据的匹配,代表着出现一次123 或者不出现123

1、在exec中使用可以获得()中的内容
const a = "123456789";
const val = /34567/.exec(a);
console.log(val[0]); // "34567"
console.log(val[1]); // undefined

const val2 = /34(56)7/.exec(a); //  ["34567", "56", index: 2, input: "123456789", groups: undefined]
console.log(val[0]); // "34567"
console.log(val[1]); // "56"
2、当正则表达式执行了test()方法或者exec() 方法,且改正则表达式中包含分组,则分组中与字符串匹配的内容,会被保存下来
  • 提取正则表达式中的内容最多提取9个()中的内容 RegExp.$1 ————RegExp.$9
const a = "123456789";
/(2)(3)((4)(5))6789/.test(a);
console.log(RegExp.$1); // "2"
console.log(RegExp.$2); // "3"
console.log(RegExp.$3); // "45" // 先匹配整体的括号(45)
console.log(RegExp.$4); // "4"   // 匹配单独的括号(4)
console.log(RegExp.$5); // "5"   // 匹配单独的括号(5)
console.log(RegExp.$6); // ""  // 因为没有第6个括号,所以是空
3、在replace中使用()可以通过$n 来取得括号中的内容
const a = "123456789";
a.replace(/234(56)78/, '这是替换的文字$1这是替换的文字!');
console.log(a); // "1这是替换的文字56这是替换的文字!"
console.log(RegExp.$1); // "56"

replace 其他字符串匹配方式:

  • $n: 匹配成功的第 n 组内容,n 是从 1 开始的自然数;
  • $&: 匹配的子字符串;
  • `$``: 匹配结果前面的文本;
  • $': 匹配结果前面的文本;
  • $$: 指代美元符号$;

分组语法

1、捕获

  • (exp): 匹配 exp , 并捕获文本到自动命名的组里,即 RegExp.$1 ————RegExp.$9

  • (?<name>exp) ||(?'name'exp): 匹配 exp , 并捕获文本到名称为 name 的组里;(见经典案例4)

  • (?:exp) : 匹配 exp , 不捕获匹配的文本;(见经典案例3)

  • (?:exp): 匹配 exp,不捕获匹配的文本(见下文经典案例3);

2、位置指定(零宽断言有用到)

  • (?=exp),匹配 exp 前面的位置;
  • (?!exp),不匹配 exp 前面的位置;
  • (?<=exp),匹配 exp 后面的位置;
  • (?<!exp),不匹配 exp 后面的位置;

3、注释

  • (?#comment),该类型组不对正则表达式的处理产生任何影响,仅供阅读注释;

零宽断言

零宽断言可以帮助我们在查找某些内容时,对内容前和内容后的信息作为判断依据,但不包含这些内容信息。

比如我们要在字符串我是小明我是小王我是小红我是小猪我啥也不是 把小明前的我是改为我不是,就可以使用零宽正先行断言(?=X), X 代表需要被匹配但不包括的内容。

const str = "我是小明我是小王我是小红我是小猪我啥也不是";
const reg = /我是(?=小明)/;
console.log(str.replace(reg, "我不是")); // "我不是小明我是小王我是小红我是小猪我啥也不是"

可以看到,(?=小明) 中的小明并没有被替换掉,但是起到了匹配的作用,这就是零宽断言的用处。

断言的写法

  • VVV(?=XXX) 零宽正先行断言 ———— 只有在断言XXXVVV的后面时,才会匹配;
  • VVV(?!XXX) 零宽负先行断言 ———— 只有在断言XXX不在 VVV的后面时,才会匹配;
  • (?<=XX)VVV 零宽正后发断言 ———— 只有在断言 XXVVV的前面时,才会匹配;
  • (?<!XX)VVV 零宽负后发断言 ———— 只有在断言 XX 不在VVV的前面时,才会匹配;

断言举例

1、vvv(?=xxx) 零宽正先行断言,例如: \b\w+(?=ing\b),匹配以ing结尾的单词的前面的部分;

2、vvv(?!xxx) 零宽负先行断言,例如: \d{3}(?!\d),匹配3位数字,且其后面不能是数字;

3、(?<=xxx)vvv 零宽正后发断言,例如: (?<=bre)\w+\b,匹配以 bre 开头的单词的后面的部分;

4、(?<!xxx)vvv 零宽负后发断言,例如:(?<![a-z])\d{7},匹配前面不是小写字母的七位数字;

正则零宽断言,速记口诀

1、= 号的,是肯定匹配的 ; ! 是否定,不匹配

2、 < 号,是写在前面并匹配前面的表达式,取后面的值;

3、不带 < 号,是写在后面并匹配后面的表达式,取前面的值;

一共有 4中

需要注意的点

  • 断言不占用字符,不被包括在内容中;
  • 断言一定要写在括号中()
  • 后发断言在以前的 js 中不支持

经典案例汇总

1、如何给一串数字用千分制表示?比如9999999999变成9,999,999,999。

const num1 = "9999999999".replace(/\d{1,3}(?=(\d{3})+$)/g, '$&,');
console.log(num1); // "9,999,999,999"
const num2 = '99999999999.02'.replace(/\d{1,3}(?=(\d{3})+(?:\.\d{1,2})?$)/g, '$&,');
console.log(num2); // "99,999,999,999.02"

2、匹配带ing的单词,但是不要ing的单词

const str = "coming soon, going gogogo, ing,goingabc";
const reg = /\b[\w]+(?=ing\b)/g; // 注意:如果 ing 后不加 \b,类似 goingabc 也会被匹配到
console.log(str.match(reg)); //  ["com", "go"]

3、非捕获性数组: (?:exp) ,即匹配 exp 但不捕获匹配的文本

const color = "#999000";
/#(?:\d+)/.test(color); // true
console.log(RegExp.$1); // ""

4、匹配 exp , 并捕获文本到名称为 name 的组里(?<name>exp) ||(?'name'exp)

"abc".replace(/(?<foo>a)/, "$<foo>-") // "a-bc",同样 $1 仍然可用