我们在开发中经常会用到正则匹配,但是里面内容庞大实在难以记清,今天我们就来梳理一下正则知识,并在最后送上一些经常用到的匹配方案,毕竟自己也是很害怕这一块的。
1.replace
关于replace的用法,其实很多,我们会仔细讲解。
var re = /apples/gi;
var str = "Apples are round, and apples are juicy.";
var newstr = str.replace(re, "oranges");
// oranges are round, and oranges are juicy.
console.log(newstr);
最基本的用法,g全局匹配,i忽略大小写。
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1");
// Smith, John
console.log(newstr);
交换了2个单词的位置
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1,$&");
// Smith, John,John Smith
\s是匹配空格,$&是匹配的子串,$1,$2分别字匹配项。
再来看一个
function replacer(match, p1, p2, p3, offset, string) {
// p1 is nondigits, p2 digits, and p3 non-alphanumerics
return [p1, p2, p3].join(' - ');
}
var newString = 'abc12345#$*%'.replace(/([^\d]*)(\d*)([^\w]*)/, replacer);
这里我们一目了然了把,看replcaer里面的参数就知道了。
我想问一下大家知道$&和replacer函数里面最后一个参数string有啥区别
其实我刚刚做了个实验,但是发现有问题,就先不说了,等下会有结论再讲吧。
function f2c(x)
{
function convert(str, p1, offset, s)
{
return ((p1-32) * 5/9) + "C";
}
var s = String(x);
var test = /(\d+(\.\d*)?)F/g;
return s.replace(test, convert);
}
函数传入一个x,将其从华氏温度转成摄氏度。
2.exec
exec是正则里面会比较频繁用到的,想想jquery的源码,里面充斥的这样的匹配。
var str = "javascript html css 555";
console.log(/(html) (css) (\d*)/.exec(str)); // ["html", index: 11, input: "javascript html css"]
output:
["html css 555", "html", "css", "555", index: 11, input: "javascript html css 555"]
可以看出来exec方法,会返回一个数组,第一个元素是符合匹配的字符串合集,第二个到第n个是表达式的子项,index是符合首个匹配的索引,input是源字符串。
var x = " a.xxx.com b.xxx.com c.xxx.com";
var re = /\s?(.*?).xxx.com/g;
while( tempR = re.exec(x))
{
console.log(tempR);
}
output:
[" a.xxx.com", "a", index: 0, input: " a.xxx.com b.xxx.com c.xxx.com"]
[" b.xxx.com", "b", index: 10, input: " a.xxx.com b.xxx.com c.xxx.com"]
[" c.xxx.com", "c", index: 20, input: " a.xxx.com b.xxx.com c.xxx.com"]
//一般用于循环,将所有的匹配都输出出来,注意(.*?)匹配任何东西。
再来最后一个例子
var myRe = /ab*/g;
var str = 'abbcdefabh';
var myArray;
while ((myArray = myRe.exec(str)) !== null) {
var msg = 'Found ' + myArray[0] + '. ';
msg += 'Next match starts at ' + myRe.lastIndex;
console.log(msg);
}
output:
Found abb. Next match starts at 3
Found ab. Next match starts at 9
注意lastIndex是指匹配的末项索引。
3.search
这个没啥其他扩展,咱们一笔带过
var str = "hello world,hello world";
// 返回匹配到的第一个位置
console.log(str.search(/hello/)); // 0
// search方法不执行全局匹配,它将忽略标志g,同时它也没有regexp对象的lastIndex的属性,且总是从字符串开始位置进行查找,总是返回的是stringObject匹配的第一个位置。
console.log(str.search(/hello/g)); //0
console.log(str.search(/world/)); // 6
search方法都是从第一个位置开始查找,并且没有全局g的标志。
4.match
初级用法和exec差不多,但是他全局匹配可以返回一个所有符合正则的数组。
var str = "hello world";
// 返回的数组内有三个元素,第一个元素的存放的是匹配的文本,还有二个对象属性;
index属性表明的是匹配文本的起始字符在stringObject中的位置;input属性声明的是
对stringObject对象的引用;
console.log(str.match("hello")); // ["hello", index: 0, input: "hello world"]
var re = new RegExp('([0-9]+)-([0-9]+)-([0-9]+)');
var str = '2016-01-02';
var result = str.match(re);
console.log(result.)); // [2016,01,02]
用了全局方法后会得到一个所有符合匹配的数组。
基本用法咱们讲解完毕,我们来看一下高阶用法。
1.贪婪模式和懒惰模式
相信大家对这个已经有所耳闻了,那我们来看到底是啥。
var txt = '<a href=”http://google.com”>谷歌</a><a href=”http://baidu.com”>百度</a>'
var re1 = /\<a (.*?)\<\/a\>/g; //懒惰模式,尽可能少的匹配
console.log(re1.exec(txt));
//["<a href=”http://google.com”>谷歌</a>", "href=”http://google.com”>谷歌"]
console.log(re1.exec(txt));
//["<a href=”http://baidu.com”>百度</a>", "href=”http://baidu.com”>百度"]
这个是懒惰模式,跟我们之前讲的exec并没有啥区别,接着看下面这个。
var re2 = /\<a (.*)\<\/a\>/g; //贪婪模式,尽可能多的匹配, 区别在不加问号
console.log(re2.exec(txt));
//["<a href=”http://google.com”>谷歌</a><a href=”http://baidu.com”>百度</a>",
//"href=”http://google.com”>谷歌</a><a href=”http://baidu.com”>百度"]
console.log(re2.exec(txt));
//null
相信大家已经看粗什么端倪来了,贪婪模式会把a标签中间所有的都检索到,所以到值exec执行一次
就没有下文了。
2.捕获分组和非捕获分组
看起来却是一脸蒙蔽,我们简短说一下吧,毕竟这个属于很少用的。
var str = "a1***ab1cd2***c2";
var reg1 = /((ab)+\d+)((cd)+\d+)/i;
var reg2 = /((?:ab)+\d+)((?:cd)+\d+)/i;
console.log(str.match(reg1));
output:
["ab1cd2", "ab1", "ab", "cd2", "cd", index: 5, input: "a1***ab1cd2***c2"]
我们发现第二层括号里面的居然也北大引出来了,我们其实只想要外层的匹配
console.log(str.match(reg2));
output:
["ab1cd2", "ab1", "cd2", index: 5, input: "a1***ab1cd2***c2"]
其实捕获和不捕获就是指的匹配想得子项是否被归入到结果中,如果还不够明白请看下面
var reg = /(?:\d{4})-(\d{2})-(\d{2})/
var reg1 = /(\d{4})-(\d{2})-(\d{2})/
var date = '2012-12-21'
reg.test(date)
RegExp.$1
"12"
奇怪了第一个匹配的\d{4}明明是2012,为什么会输出12呢
reg1.test(date)
RegExp.$1
“2012”
反正我已经是领略到了,如果还有疑问可以看mdn开发文档
3.零宽断言
零宽断言就是下结论,例如ab,正则:a(?=b),匹配a并且向右看是b,得到的结果是a,断言不会在匹配的内容当中,如果是a(?=c),则匹配不到任何内容,因为匹配a以后向右看并不是c。另外,零宽断言分两种:前瞻(Lookahead)和后顾(Lookbehind);但JavaScript只支持前瞻。
// 获取字符串中以ing结尾的单词的前半部分
var str = 'I love dancing but he likes playing';
var pattern = /\b\w+(?=ing\b)/g;
var ans = str.match(pattern);
console.log(ans);
output:
[danc,play]
// 获取第五位不是i的单词的前四位
var s = 'I love dancing but he likes singing';
var pattern = /\b\w{4}(?!i)/g;
var ans = s.match(pattern);
console.log(ans); // ["love", "like"]
下面以一个很经典的例子结束本期
var str = '1234567890'
'12334565632'.replace(/(\d)(?=(\d{3})+$)/g, "$1,");
output:
"12,334,565,632"
记住要加上g,不然结果是12,334565632
又要结束了,下期会给大家带来前端工具gulp的文章。