正则表达式详解

检查是否有匹配项

regexp.test(str):返回true或false
str.search(regexp):匹配成功则返回第一个匹配项的索引,否则返回-1

返回所有匹配项

regexp.exec(str)
与match方法返回的结果相同,但是可以多次调用exec方法来获取下一个捕获组的信息。在全局模式下也通用,但match方法在全局模式下不会获取捕获组。
若想匹配多个结果需要多次调用exec方法,下一次查找将从lastIndex位置开始。
如果匹配成功,返回一个数组,并更新regexp对象的属性。如果匹配失败,exec() 方法返回 null。

返回的数组将完全匹配成功的文本作为第一项,将正则括号里匹配成功的捕获组填充到数组后面的项中。
数组的index属性:匹配到的字符的索引值
数组的input属性:原始字符串
(正则对象)lastIndex:下一次匹配的起始位置
(正则对象)ignoreCase、global、multiline:是否使用了相应模式

str.match(regexp)
用于匹配检索项,返回一个包含匹配结果的数组。

如果正则表达式没有 g 标志,则 str.match() 会返回和 RegExp.exec() 相同的结果。而且返回的 Array 拥有一个额外的 input 属性,该属性包含被解析的原始字符串。另外,还拥有一个 index 属性,该属性表示匹配结果在原字符串中的索引
如果正则表达式包含 g 标志,则该方法返回一个 Array ,它包含所有匹配的子字符串。捕获组不会被返回
如果没有匹配到,则返回 nul

替换

str.replace(regexp|substr, newSubStr|function)

第一个参数若是正则表达式并指定了g,那么该正则所匹配的所有内容都会被第二个参数替换
第一个参数是字符串,则仅第一个匹配会被替换。
该方法并不改变调用它的字符串本身,而只是返回一个新的替换后的字符串。

基础知识

简写形式

\d 就是[0-9]。// 表示是一位数字。记忆方式:其英文是digit(数字)。
\D 就是[^0-9]。// 表示除数字外的任意字符。
\w 就是[0-9a-zA-Z_]。// 表示数字、大小写字母和下划线。记忆方式:w是word的简写,也称单词字符。
\W 是[^0-9a-zA-Z_]。// 非单词字符。
\s 是[ \t\v\n\r\f]。// 表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。记忆方式:s是space character的首字母。
\S 是[^ \t\v\n\r\f]。 // 非空白符。
. 就是[^\n\r\u2028\u2029]。// 通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。记忆方式:想想省略号...中的每个点,都可以理解成占位符,表示任何类似的东西。

如果要匹配任意字符怎么办?可以使用[\d\D]、[\w\W]、[\s\S]和[^]中任何的一个。

1.  量词

{m,} // 表示至少出现m次。
{m} // 等价于{m,m},表示出现m次。
? // 等价于{0,1},表示出现或者不出现。记忆方式:问号的意思表示,有吗?
+ // 等价于{1,},表示出现至少一次。记忆方式:加号是追加的意思,得先有一个,然后才考虑追加。
* // 等价于{0,},表示出现任意次,有可能不出现。记忆方式:看看天上的星星,可能一颗没有,可能零散有几颗,可能数也数不过来。

2. 惰性匹配
量词默认是贪婪型匹配的,也就是会匹配尽可能长的结果。如果我们需要匹配到第一个就停止,那么就要使用惰性匹配,在后面加一个问号:

{m,n}? 
{m,}?

3. 多选分支是惰性的

var regex = /good|nice/g;
var string = "good idea, nice try";
string.match(regex);
//["good","nice"]
var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) ); 
// => ["good"]

可以发现只匹配了good就结束了。
如果更改一下:

var regex = /goodbye|good/g;
var string = "goodbye";
console.log( string.match(regex) ); 
// => ["goodbye"]

这次只匹配了goodbye,说明分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。

4. 匹配开头和结束

^ // 匹配开头,在多行匹配中匹配行开头。
$ // 匹配结尾,在多行匹配中匹配行结尾。

比如可以将开头和结尾用#替换:

var result = "hello".replace(/^|$/gm,"#");
//"#hello#"

5. 使用括号的几种情况
1.分组:
其中括号是提供分组功能,使量词+作用于“ab”这个整体

/(ab)+/

2.分支结构
提供了子表达式的所有可能。

/(p1|p2)/

3.提取数据
比如我们写了这样一个正则

/\d{4}-\d{2}-\d{2}/

如果给其中每一项都加上括号的话,后序就可以提取具体信息:

var reg = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
string.match(reg);

//["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]

match方法返回的数组,第一项是整体匹配结果,然后是各个分组(括号里)的匹配内容。因此加上括号可以让我们获取分组的内容。

注意,这里要是有g标志,只会返回[“2017-06-12”],而不会返回分组信息。

另外也可以使用正则对象的exec方法:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
regex.exec(string);
//["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]

不含g标志时,match和exec的结果相同。

同时,也可以使用构造函数的全局属性1至1至9来获取:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";

regex.test(string); // 一些正则操作

console.log(RegExp.$1); // "2017"
console.log(RegExp.$2); // "06"
console.log(RegExp.$3); // "12"

4.替换
比如,想把yyyy-mm-dd格式,替换成mm/dd/yyyy怎么做?

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";

var result = stirng.replace(regex,"$2/$3/$1");
//"06/12/2017"

在replace的第二个参数中,可以用$1、$2、$3指代对应的分组。
这里也顺带一提replace第二参数为函数时的用法。此函数有以下几个参数:

因此这里的替换也可以这样完成:

var result = string.replace(regex,function(match,year,month,day){
return month+"/"+day+"/"+year;
})

5.反向引用
也可以在正则本身里引用分组,但是只能引用之前出现的分组,即反向引用。
比如要写一个正则支持匹配如下三种格式:

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}/
var string = "2016-06/12";
regex.test(string); //false 

注意里面的\1,表示的引用之前的那个分组(-|\/|.),不管它匹配到什么,\1都匹配相同的字符。

6.不需要被捕获的分组
由于小括号既可以表示分组又可以表示捕获组,有什么方法能够表示一个小括号只是表示分组而不捕获它呢?只要在括号的开头加上?:即可:

var reg = /((?:ninja-)+)sword/;
var ninjas = "ninja-ninja-sword".match(reg);
//ninjas[0]:"ninja-ninja-sword"
//ninjas[1]:"ninja-ninja-"

可以看到只不活了最外面的括号中的内容,最里面的括号并没有被捕获。

实战案例
匹配16进制颜色值

/#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})/g

其中的字符可以出现三次或六次

匹配时间
24小时制的,如23:59 、02:01

/^([01][0-9]|[2][0-3]):[0-5][0-9]$/

注意表示小时的前两位数,当第一位为0或1时,第二位可以是0-9,但是第一位为2时,后面只能是0-3.

匹配日期 yyyy-mm-dd
年,四位数字即可,可用[0-9]{4}。

月,共12个月,分两种情况01、02、……、09和10、11、12,可用(0[1-9]|1[0-2])。

日,最大31天,可用(0[1-9]|[12][0-9]|3[01])。

/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/

注意是要一位一位匹配!像25这种日期需要使用[1-2][0-9]|来匹配,一开始写了[1-31]的我是个傻子……

匹配id
要求从<div id="container" class="main"></div>中提取出id=”container”。
可能最开始想到的正则是:/id=".*"/,但是首先通配符.就是包含双引号的,其次*则会贪婪匹配,因此最后匹配的结果会是id="container" class="main".

可以改用惰性匹配:/id=".*?"/
也有另一种效率高的方法:/id="[^"]*"/ (网易2018秋招笔试中出现类似题目了),也就是在双引号中匹配的任意字符中除去双引号。

数字的千位分隔符
比如把”12345678”,变成”12,345,678”。

这里要使用到(?=p),其中p是一个子模式,即p前面的位置。比如(?=l),表示’l’字符前面的位置。

var result = "hello".replace(/(?=l)/g, '#');
// "he#l#lo"

这样针对千位分隔符,我们可以先弄出最后一个逗号:

var result = "12345678".replace(/(?=\d{3}$)/g,',');
//"12345,678"

现在试图弄出所有逗号,因为逗号出现的位置,要求后面3个数字一组,也就是\d{3}至少出现一次,使用加号:

var result = "12345678".replace(/(?=(\d{3})+$)/g,',');
console.log(result); 
// => "12,345,678"

但是这个模式匹配123456789,会造成 “,123,456,789”的结果,因此要去除开头。
这里要使用到(?!p),其实就是(?=p)的反模式,即匹配除p之前的其他位置。这里就可以使用(?!^)来除去开头

var result = "123456789".replace(/(?!^)(?=(\d{3})+$)/g,',');
//"123,456,789"

字符串trim方法
匹配到开头和结尾的空白符,然后替换成空字符。

str.replace(/^\s+|\s+$/g,'');

也可以匹配整个字符串,然后使用引用分组来提取中间的内容:

str.replace(/^\s*(.*?)\s*$/g,$1);

将fontFamily这种驼峰表示法改为font-family这种形式:
使用$1获取前面捕获的内容

var str = "fontFamily";
str.replace(/([A-Z])/g,"-$1").toLowerCase();
//font-family

同样也可以进行反方向的操作,即把font-family转换为驼峰形式:
这里可以使用replace第二个参数为函数的形式:

var str = "font-family";
str.replace(/-(\w)/g,function(match,letter){
return letter.toUpperCase();
})
//"fontFamily"

虽然这个例子中只有一个匹配,但是replace的这种使用方法中每一个匹配都会调用此函数。

匹配所有字符,包括换行符
一般我们使用.来匹配所有字符,但是这个操作符却无法匹配换行符。因此想真正匹配所有字符包括换行符的话,要使用其他方案:

/[\s\S]*/

\s匹配所有空白符,\S匹配不是空白符的字符,因此最终的结果就是二者匹配所有字符。

/(.|\s)*/

.匹配除换行符之外的所有字符,\s匹配包括换行符在内的空白字符,因此最终的结果是会匹配包含换行符在内的所有字符。

posted @ 2018-12-23 10:20  会写代码的赖先生  阅读(533)  评论(0编辑  收藏  举报