![6b34d2d9741189dd9836dff67a1dcd2f.png](https://i-blog.csdnimg.cn/blog_migrate/ed44429a2096d3e8f66457c46998b20a.jpeg)
前言
大家学生时代在数学卷子上的填空题,可能会遇到这样的找规律题目,比如最简单的等差数列:
已知数列:1,5,9,13,17…,求该数列的表达式是 ___________ 答案是显而易见的:4*n-3 (n为整数)
我刚接触正则表达式的时候,很自然地想到了这里。
我们构建的正则表达式,比如 /c{2,5}ode/
很类似于数列的表达式,要去匹配的字符串比如 "code"
、"ccode"
、"cccode"
很类似于数列中的数字。
所以正则表达式是对目标字符串们的共性,抽象出来的表达!
正则表达式的身影出现在各种开发语言中…这里,我们走入 JavaScript 的正则世界。在开始之前,我给大家分享两个正则的在线工具:
- 可视化正则,且支持可视化效果图片导
- 支持保存与分享正则、输入字符串看匹配情况,还有打怪测试
Let’s Go!!!
正则匹配的内容
正则匹配的内容有两部分,一个是字符,一个是位置。
字符很好理解,比如 "code"
中的c
、o
;位置呢,指的是相邻字符之间的空隙,比如c
和o
之间、o
和d
之间,另外还有特殊的像开始与结束:^
和$
,只有一个边是字符。
这里画了一张匹配字符和位置的小结:
![750073f1e752dfbb669ecd76062eb4d6.png](https://i-blog.csdnimg.cn/blog_migrate/39127b924e675c205d4286b9cd861e1a.jpeg)
Demo
匹配字符demo:
- 匹配颜色16进制:这里有横向匹配,使用了量词
{6}
、{3}
;有纵向匹配,使用了字符组[0-9A-Fa-f]
;有多选分支,使用了管道符|
![a70f8109a697f3d33a821b82a9b7776e.png](https://i-blog.csdnimg.cn/blog_migrate/c598ad7f1e58dcf5576ba902c023833c.jpeg)
- 匹配html中的id值-方法1:比如
'<div id="container" class="main"></div>'
要找出id="container"
。 这里在量词后加?
是开启了惰性匹配,惰性匹配是尽可能吃得少,而默认的贪婪匹配是尽可能吃得多,试试链接中去掉问号的结果
![152516a1304957c77a48c270b2f86955.png](https://i-blog.csdnimg.cn/blog_migrate/156eb04470fef3e9ae297a5cc31c1e79.jpeg)
- 匹配html中的id值-方法2:(条条大路通罗马)这里用排除字符组明确排除双引号之间不能再有双引号
![98d7ab470ee3c6a7f8c82bdeb0fb974e.png](https://i-blog.csdnimg.cn/blog_migrate/9874f836675ccbf306c2c5afde374b7e.jpeg)
匹配位置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
这样的情况;
![222ee41ef401b1d2dd64064b800381c6.png](https://i-blog.csdnimg.cn/blog_migrate/2569d4e970ee378da0d85fb8f52ef9a6.jpeg)
- 数字的千位分割符表示-2:这里用Postive lookbehind
(?<=d)
代替了上一个例子中的(?!^)
,这里有一个细节是当多个位置放在一起时,看作对这个位置的多个限制条件。 - 数字的千位分割符表示-3:这里用了单词和非单词边界,可以匹配多个数字。
![c8d10ea0c87ed18be1f9e1e4656b869d.png](https://i-blog.csdnimg.cn/blog_migrate/fee95463947520d44e4fbfe6c87f7637.jpeg)
工具:括号
括号除了提高优先级以外,还有一个重要的功能:分组。以便我们能引用它:
- 在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}1d{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#split
str#match
:有g
,返回的是所有匹配的内容;没有g
,返回,首个匹配结果 + 捕获组数据(没有,返回null
)+ 匹配结果开始位置 + 搜索的字符串;没有匹配返回null
;str#replace
regex#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正则表达式完整教程:感谢老姚
入门教程