[JS正则迷你书作者](老姚 的个人主页 - 动态 - 掘金 (juejin.cn))
[JS正则迷你书](《JavaScript 正则表达式迷你书》问世了! - 知乎 (zhihu.com))
括号
分组和分支结构
// 分组,匹配ab
var regex = /(ab)+/
// 分支结构
var regex = /I Love (Javascript|Regular Expression )/
分组引用
这是括号一个重要的作用,有了它,我们就可以进行数据提取,以及更强大的替换操作。而要使用它带来的好处,必须配合使用实现环境的 API。
以日期为例。假设格式是yyyy-mm-dd的,我们可以先写一个简单的正则:
var regex = /\d{4}-\d{2}-\d{2}/
var regex = /(\d{4})-(\d{2})-(\d{2})/
对比这两个可视化图片,我们发现,与前者相比,后者多了分组编号,如Group #1。
其实正则引擎也是这么做的,在匹配过程中,给每一个分组都开辟一个空间,用来存储每一个分组匹配到的数据。
既然分组可以捕获数据,那么我们就可以使用它们。
提取数据
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
console.log(string.match(regex));
// => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]
regex3 = /(\d{4})-(\d{2})-(\d{2})/g
console.log(string2.match(regex3));
// => ["2017-06-12"]
match返回的一个数组,第一个元素是整体匹配结果,然后是各个分组(括号里)匹配的内容,然后是匹配下标,最后是输入的文本。另外,正则表达式是否有修饰符g,match返回的数组格式是不一样的
另外也可以使用正则实例对象的exec方法:
var regex = /(\d{4})-(\d{2})-(\d{2})/g;
var string = "2017-06-12";
console.log(regex.exec(string));
// => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]
// 加不加g都一样
同时,也可以使用构造函数的全局属性9来获取(必须要先有正则操作,最后输出显示的会是最后一句返回true的或者匹配的上的正则操作):
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
regex.test(string);// 正则操作即可,例如
//regex.exec(string);
//string.match(regex);
console.log(RegExp.$1);
// "2017"
console.log(RegExp.$2);
// "06"
console.log(RegExp.$3);
// "12"
// 有$1-...-$_
// $_表示输入的文本
替换数据
比如,想把yyyy-mm-dd格式,替换成mm/dd/yyyy。
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
var result = string.replace(regex, "$2/$3/$1");
// 将string中的regex匹配到的变成$2/$3/$1的形式
console.log(result);
// => "06/12/2017"
相当于
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
var result = string.replace(regex, function(){
return RegExp.$2 + "/" + RegExp.$3 + "/" + RegExp.$1;
});
console.log(result);
// => "06/12/2017"
// 也等价于:
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
var result = string.replace(regex, function(match, year, month, day){
// match表示匹配到的总字符串
// 具体的参数数量看有几个编组
return month + "/" + day + "/" + year;
});
console.log(result);
// => "06/12/2017"
反向引用-\1\2
除了使用相应 API 来引用分组,也可以在正则本身里引用分组。但只能引用之前出现的分组,即反向引用。
还是以日期为例。比如要写一个正则支持匹配如下三种格式:
2016-06-12
2016/06/12
2016.06.12
最先可能想到的正则是:
var regex = /\d{4}(-|/|.)\d{2}(-|/|.)\d{2}/;
其中/和.需要转义。虽然匹配了要求的情况,但也匹配"2016-06/12"这样的数据。
假设我们想要求分割符前后一致怎么办?
此时需要使用反向引用:
var regex = /\d{4}(-|/|.)\d{2}\1\d{2}/;
// 其中的\1表示引用编组1,且匹配到的必须与编组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
\10表示?
\10表示引用第十个编组,所以要是想分别匹配 \1 和 0,需要这么写: (?:\1)0 或 \1(?:0) (非捕获括号)
引用不存在的分组?
匹配反向引用的字符本身。例如\2,就匹配"\2"。
注意"\2"表示对"2"进行了转义。
不同的浏览器和版本,打印的结果不一样。
分组后边有量词?
分组后面有量词的话,分组最终捕获到的数据是最后一次的匹配。
var regex = /(\d)+/;
var string = "12345";
console.log(string.match(regex));
// => ["12345", "5", index: 0, input: "12345"]
所以用到反向引用的时候,也需要注意
var regex = /(\d)+ \1/;
console.log(regex.test("12345 1"));
// => false
console.log(regex.test("12345 5"));
// => true
非捕获括号
之前出现的括号,都会捕获它们匹配到的数据,以便后续引用,因此也称它们是捕获型分组和捕获型分支。
如果只想要括号最原始的功能,但不会引用它,即,既不在 API 里引用(RegExp.$1、exec方法、match方法),也不在正则里反向引用(\1\2\3... )。
此时可以使用非捕获括号 (?:p) 和 (?:p1|p2|p3) 。
var regex = /(?:ab)+/g; // 原:/(ab)+/g
var string = "ababa abbb ababab";
console.log(string.match(regex));
// => ["abab", "ab", "ababab"]