var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/; var string1 = "2017-06-12"; var string2 = "2017/06/12"; var string3 = "2017.06.12"; var string4 = "2016-06/12"; console.log( regex.test(string1) ); // true console.log( regex.test(string2) ); // true console.log( regex.test(string3) ); // true console.log( regex.test(string4) ); // false
注意里面的\1,表示的引用之前的那个分组(-|\/|\.)。不管它匹配到什么(比如-),\1都匹配那个同样的具体某个字符。
我们知道了\1的含义后,那么\2和\3的概念也就理解了,即分别指代第二个和第三个分组。
看到这里,此时,恐怕你会有三个问题。
3.1 括号嵌套怎么办?
以左括号(开括号)为准。比如:
var regex = /^((\d)(\d(\d)))\1\2\3\4$/; var string = "1231231233"; console.log( regex.test(string) ); // true console.log( RegExp.$1 ); // 123 console.log( RegExp.$2 ); // 1 console.log( RegExp.$3 ); // 23 console.log( RegExp.$4 ); // 3
我们可以看看这个正则匹配模式:
第一个字符是数字,比如说1,
第二个字符是数字,比如说2,
第三个字符是数字,比如说3,
接下来的是\1,是第一个分组内容,那么看第一个开括号对应的分组是什么,是123,
接下来的是\2,找到第2个开括号,对应的分组,匹配的内容是1,
接下来的是\3,找到第3个开括号,对应的分组,匹配的内容是23,
最后的是\4,找到第3个开括号,对应的分组,匹配的内容是3。
这个问题,估计仔细看一下,就该明白了。
3.2 \10表示什么呢?
另外一个疑问可能是,即\10是表示第10个分组,还是\1和0呢?答案是前者,虽然一个正则里出现\10比较罕见。测试如下:
var regex = /(1)(2)(3)(4)(5)(6)(7)(8)(9)(#) \10+/; var string = "123456789# ######" console.log( regex.test(string) );
3.3 引用不存在的分组会怎样?
因为反向引用,是引用前面的分组,但我们在正则里引用了不存在的分组时,此时正则不会报错,只是匹配反向引用的字符本身。例如\2,就匹配"\2"。注意"\2"表示对2进行了转意。
var regex = /\1\2\3\4\5\6\7\8\9/; console.log( regex.test("\1\2\3\4\5\6\7\8\9") ); console.log( "\1\2\3\4\5\6\7\8\9".split("") );
chrome浏览器打印的结果:
4. 非捕获分组
之前文中出现的分组,都会捕获它们匹配到的数据,以便后续引用,因此也称他们是捕获型分组。
如果只想要括号最原始的功能,但不会引用它,即,既不在API里引用,也不在正则里反向引用。此时可以使用非捕获分组(?:p),例如本文第一个例子可以修改为:
var regex = /(?:ab)+/g; var string = "ababa abbb ababab"; console.log( string.match(regex) ); // ["abab", "ab", "ababab"]
5. 相关案例
至此括号的作用已经讲完了,总结一句话,就是提供了可供我们使用的分组,如何用就看我们的了。
5.1 字符串trim方法模拟
trim方法是去掉字符串的开头和结尾的空白符。有两种思路去做。
第一种,匹配到开头和结尾的空白符,然后替换成空字符。如:
function trim(str) { return str.replace(/^\s+|\s+$/g, ''); } console.log( trim(" foobar ") ); // "foobar"
第二种,匹配整个字符串,然后用引用来提取出相应的数据:
function trim(str) { return str.replace(/^\s*(.*?)\s*$/g, "$1"); } console.log( trim(" foobar ") ); // "foobar"
这里使用了惰性匹配*?,不然也会匹配最后一个空格之前的所有空格的。
当然,前者效率高。
5.2 将每个单词的首字母转换为大写
function titleize(str) { return str.toLowerCase().replace(/(?:^|\s)\w/g, function(c) { return c.toUpperCase(); }); } console.log( titleize('my name is epeli') ); // "My Name Is Epeli"
思路是找到每个单词的首字母,当然这里不使用非捕获匹配也是可以的。
5.3 驼峰化
function camelize(str) { return str.replace(/[-_\s]+(.)?/g, function(match, c) { return c ? c.toUpperCase() : ''; }); } console.log( camelize('-moz-transform') ); // MozTransform