正则表达式详解
检查是否有匹配项
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匹配包括换行符在内的空白字符,因此最终的结果是会匹配包含换行符在内的所有字符。