三、正则表达式括号的使用

简单理解:括号提供了分组,便于我们使用它。

通常有两种引用情况:在JS代码中引入,和在正则表达式中引入

分组和分支结构,主要是强调括号内是一个整体,即提供子表达式。

  • 分组如 /(ab)+/g 匹配连续出现的 ab
  • 分支结构如 /(a|b)+/g 匹配出现的 ab 表达式。

1.分组引用

如在日期匹配的时候,就可以这么改造:

  1. // 原来
  2. let r = /\d{4}-\d{2}-\d{2}/;
  3. // 现在
  4. let r = /(\d{4})-(\d{2})-(\d{2})/;
  • 提取数据
  1. "2019-03-14".match(r);
  2. r.exec("2019-03-14");
  3. // ["2019-03-14", "2019", "03", "14", index: 0, input: "2019-03-14"]
  4. RegExp.$1; // "2019"
  5. RegExp.$2; // "03"
  6. RegExp.$3; // "14"
  • 替换

yyyy-mm-dd 转成 mm/dd/yyyy

  1. "2019-03-14".replace(r, "$2/$3/$1");
  2. // 等价于
  3. "2019-03-14".replace(r, function(){
  4. return RegExp.$2 + '/' + RegExp.$3 + '/' + RegExp.$1;
  5. });

2. 反向引用

使用 \n 表示第 n 个分组,比如 \1 表示第 1 个分组:

  1. let r = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
  2. r.test("2019-03-15");
  3. r.test("2019/03/15");
  4. r.test("2019.03.15");
  5. r.test("2019-03/15");
  • 多个括号嵌套

按照开括号的顺序:

  1. let r = /^((\d)(\d(\d)))\1\2\3\4$/;
  2. let s = "1231231233";
  3. r.test(s);
  4. console.log([RegExp.$1,RegExp.$2,RegExp.$3,RegExp.$4]);
  5. // ["123", "1", "23", "3"]
  • 特殊情况

\10 表示的是第 10 个分组,若要匹配 \0 时,使用 (?:\1)0\1(?:0)

  1. let r = /(1)(2)(3)(4)(5)(6)(7)(8)(9)(#) \10+/;
  2. let s = "123456789# #####";
  3. r.test(s); // true
  • 当引用不存在的分组

如匹配 \2 是前面不存在,则匹配 \2 本身,即对 2 的转义,不同浏览器可能不同:

  1. let r = /\1\2\3\4/;
  2. r.test("\1\2\3\4"); // true
  3. "\1\2\3\4".split('');// ["", "", "", ""]
  • 分组后面有量词

当分组后面有量词的话,则捕获的是最后一次的匹配:

  1. "12345".match(/(\d)+/); // ["12345", "5", index: 0, input: "12345"]
  2. /(\d)+ \1/.test("12345 1"); // false
  3. /(\d)+ \1/.test("12345 5"); // true

3. 向后引用

\n 表示后向引用, \1 是指在正则表达式中,从左往右数第1个 () 中的内容;以此类推, \2 表示第2个 ()\0 表示整个表达式

  1. let rgx = /\d{4}(\-|\/|.)\d{1,2}\1\d{1,2}/
  2. rgx.test("2020-01-05") // true
  3. rgx.test("2020-01.05") // false

上面案例中,\1代表重复 (\-|\/|.)

4. 相关案例

这里只写出核心代码。

  • 模拟字符串 trim 方法
  1. // 1 匹配首尾空白符,替换成空字符
  2. " aaa ".replace(/^\s+|\s+$/g, ""); // "aaa"
  3. // 2 匹配整个字符串,再用引用提取对应数据
  4. " aaa ".replace(/^\s*(.*?)\s*$/g, "$1");// "aaa"
  • 每个单词首字母大写
  1. "hi leo hi boy!".toLowerCase().replace(
  2. /(?:^|\s)\w/g,
  3. c => c.toUpperCase()
  4. );
  5. // "Hi Leo Hi Boy!"
  • 驼峰化 和 中划线化
  1. "-leo-and-pingan".replace(/[-_\s]+(.)?/g,
  2. (match, c) => c ? c.toUpperCase() : ''
  3. );
  4. // "LeoAndPingan"
  5. "LeoAndPingan".replace(/([A-Z])/g, "-$1").replace(
  6. /[-_\s]+g/,"-"
  7. ).toLowerCase();
  8. // "-leo-and-pingan"
  • 匹配成对HTML标签

匹配成对标签 <h1>leo<\h1>,而不匹配不成对标签 <h1>leo<\h2>

  1. let r = /<([^>]+)>[\d\D]*<\/\1>/;
  2. r.test("<h1>leo leo leo</h1>"); // true
  3. r.test("<a>leo leo leo</a>"); // true
  4. r.test("<h1>leo leo leo</h2>"); // false