前言
大家学生时代在数学卷子上的填空题,可能会遇到这样的找规律题目,比如最简单的等差数列:
已知数列:1,5,9,13,17…,求该数列的表达式是________
答案是显而易见的:4*n-3 (n为整数)
我刚接触正则表达式的时候,很自然地想到了这里。
我们构建的正则表达式,比如 /c{2,5}ode/ 很类似于数列的表达式,要去匹配的字符串比如 "code"、"ccode"、"cccode" 很类似于数列中的数字。
所以正则表达式是对目标字符串们的共性,抽象出来的表达!
正则表达式的身影出现在各种开发语言中…这里,我们走入 JavaScript 的正则世界。在开始之前,我给大家分享两个正则的在线工具:
- JavaScript Regular Expression Visualizer:可视化正则、支持可视化效果图片导出
- Online regex tester and debugger: PHP, PCRE, Python, Golang and JavaScript:支持保存与分享正则、打怪测试
Let’s Go!!!
正则匹配的内容
正则匹配的内容有两部分,一个是字符,一个是位置。
字符很好理解,比如 "code"中的c、o;位置呢,指的是相邻字符之间的空隙——c和o之间、o和d之间,另外还有特殊的像开始与结束:^和$,只有一个边是字符。
这里画了一张匹配字符和位置的小结:

Demo
匹配字符demo:
- 匹配颜色16进制:这里有横向匹配,使用了量词
{6}、{3};有纵向匹配,使用了字符组[0-9A-Fa-f];有多选分支,使用了管道符|
- 匹配html中的id值-方法1:比如
'<div id="container" class="main"></div>'要找出id="container"。 这里在量词后加?是开启了惰性匹配,惰性匹配是尽可能吃得少,而默认的贪婪匹配是尽可能吃得多,试试链接中去掉问号的结果👀
- 匹配html中的id值-方法2:(条条大路通罗马)这里用排除字符组明确排除双引号之间不能再有双引号

匹配位置demo:
需求:数字的千位分隔符表示,比如输入"12345678",转为12,345,678。我们会用到str#replace:
var result = "123456789".replace(/.../)/g, ',')
console.log(result);
正则们:
- 数字的千位分割符表示-方法1:这里用了结束位置符
$,从数字末尾开始;用了Positive lookahead(?=(\d{3})),找到每3位数字后面的位置;用了Negative lookahead(?!^),排除,123,456,789这样的情况;
- 数字的千位分割符表示-2:这里用Postive lookbehind
(?<=\d)代替了上一个例子中的(?!^),这里有一个细节是当多个位置放在一起时,看作对这个位置的多个限制条件。 - 数字的千位分割符表示-3:这里用了单词和非单词边界,可以匹配多个数字。

工具:括号
括号除了提高优先级以外,还有一个重要的功能:分组。以便我们能引用它:
- 在JavaScript中引用
- 在正则表达式里引用
简单demo:在JavaScript中引用
注意这里的(?:p)是非捕获分组,这样的分组不能被捕获,也不能被引用
//模拟trim方法:在JavaScript中引用分组
function trim(str) {
return str.replace(/^\s*(.*?)\s*$/g, "$1");
}
console.log( trim(" foobar ") );
//将单词首字母大写:在JavaScript中引用分组
function titleize(str) {
return str.toLowerCase().replace(/(?:^|\s)\w/g, function(c) {
return c.toUpperCase();
});
}
// console.log( titleize('My name is harden') );
简单demo:在正则表达式里引用
保证分割符前后一致
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
JavaScript API使用注意事项
一共有6个API:
str#search:返回首次匹配的位置,未找到返回-1;str#splitstr#match:有g,返回的是所有匹配的内容;没有g,返回,首个匹配结果 + 捕获组数据(没有,返回null)+ 匹配结果开始位置 + 搜索的字符串;没有匹配返回null;str#replaceregex#test:有匹配返回true,没有匹配返回false;regex#exec:返回 当前匹配结果 + 捕获组数据(没有,返回null)+ 匹配结果开始位置 + 搜索的字符串
和正则有关的操作(罗列推荐使用的方法):
- 验证:
regex#test - 切分:
str#split - 提取(提取括号中内容):
str#match和regex#exec差不多,另外str#replace在参数函数中获取数据也方便; - 替换:
str#replace
注意事项:
str#search和str#match都支持字符串和正则作参数,在字符串作参数时会自动字符串转正则。遇到元字符要转义:
var string = "2017.06.27";
console.log( string.search(".") );
// => 0
//需要修改成下列形式之一
console.log( string.search("\\.") );
console.log( string.search(/\./) );
// => 4
// => 4
regex#exec和str#match比较:在没有修饰符g时,使用str#match可以获取完整信息,如果有修饰符g时,也想获得完整信息,用一下regex#exec:
var string = "2017.06.27";
var regex2 = /\b(\d+)\b/g;
var result;
while ( result = regex2.exec(string) ) {
console.log( result, regex2.lastIndex );
}
// => ["2017", "2017", index: 0, input: "2017.06.27"] 4
// => ["06", "06", index: 5, input: "2017.06.27"] 7
// => ["27", "27", index: 8, input: "2017.06.27"] 10
结尾
我猜这里的demo们的信息量有一点点爆炸💥 👀,凝聚就是精华!
基础篇包含了:
- 匹配内容:匹配字符或者位置(量词、字符组、贪婪&惰性匹配、排除字符组 balabalaba)
- 括号 -> 引用
- JavaScrip的正则API们
最后扔两个非常有意思的函数,和这群API中某两个相关:
字符串压缩:str#replace
//str#replace做压缩
function compress(source) {
var keys = {};
source.replace(/([^=&]+)=([^&]*)/g, function(full, key, value) {
keys[key] = (keys[key] ? keys[key] + ',' : '') + value;
});
var result = [];
for (var key in keys) {
result.push(key + '=' + keys[key]);
}
return result.join('&');
}
console.log( compress("a=1&b=2&a=3&b=4") );
// => "a=1,3&b=2,4"
类型判断:字符串做信息存储,str#split
//类型判断:使用字符串存储信息
var utils = {};
"Boolean|Number|String|Function|Array|Date|RegExp|Object|Error".split("|").forEach(function(item) {
utils["is" + item] = function(obj) {
return {}.toString.call(obj) == "[object " + item + "]";
};
});
console.log( utils.isArray([1, 2, 3]) );
// => true
参考链接
老姚-JS正则表达式完整教程:感谢老姚🙏