组合

用括号表示对正则进行分组,括号内的元字符被看成是一个整体

例子:
匹配 MAC 地址,mac 规则:xx:xx:xx:xx:xx:xx

  1. // 不用组合
  2. let reg1 = /[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}/
  3. // 使用组合
  4. let reg2 = /([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}/

第二个正则表达括号内的 [0-9a-fA-F]{2}: 是一个整体,需要匹配5次

分组与编号

括号在正则中可以用来分组,括号中的子表达式是一个子组

用 JS 来验证分组与编号的规则

  1. let reg = /(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})/
  2. let str = '2020-09-18 17:29:00'
  3. reg.exec(str)

上面的匹配结果为
image.png
从匹配结果来看,编号为 0 的结果是整个正则表达式的匹配结果,而括号中的子表达式的匹配结果编号则分别是 1 和 2。也就是说,括号中子表达式匹配的结果也会保存下来,如果不想保存某个子表达式的匹配结果,则可以在括号里加前缀 ?: ,如下图

image.png
在第二个子表达式添加 ?: 前缀,就不会将子表达式的匹配结果保存下来。

括号嵌套

如果是括号嵌套,那分组的编号又是如何定义的呢?
上面的例子,只是将正则在桥套一些括号

  1. let reg = /((\d{4})-(\d{2})-(\d{2})) ((\d{2}):(\d{2}):(\d{2}))/

结果如图
image.png
所以,如果是括号嵌套的话,那么分组的编号规则就是跟树结构差不多,子括号就是相当于子节点,整个正则表达式就是根节点,编号为0。从根节点(整个正则表达式)开始,分配编号 0,然后是左边第一个括号,分配为1,如果左边第一个括号内有子括号,那么以左边第一个括号为根括号,编号为 1,继续遍历子括号并分配编号。这个编号分配规则就跟树的深度遍历是差不多的。
image.png

引用

上面已经讲了分组的编号问题,那分组的编号具有什么作用呢?
在使用正则进行匹配或替换时,可以利用 \number 的方式来引用正则的分组,而 JavaScript 的则是利用 $number 来进行引用

正则引用在查找中的使用

JavaScript 的引用在查找使用 **\number** 的。
所以这里使用 PHP 来进行举例
image.png
如上图,分组 (\w+) 的编号是1,在正则表达式中, 通过 \1 来对分组1进行引用,达到匹配连续两个相同单次的效果。
注意:引用不是复制正则表达式,而是复制正则表达式匹配的字符串,上述因为是全局匹配,所以匹配两次,上述正则表达式的可以表示为 (cat cat)|(hat hat)

正则引用在替换中使用

JS 中的正则引用替换其实挺使用的,很多时候,我们都需要将 12345678 转换成 12,345,678 这种格式

  1. '12345678'.replace(/(\d)(?=(\d{3})+$)/g, '$1,')
  2. // "12,345,678"

总结

组合和引用 - 图6