正则(regular expression)描述了一种字符串的匹配式。一般应用在一些方法中,用一些特殊的符号去代表一些特定的内容,对字符串中的信息实现查找,替换,和提取的操作。js中的正则表达式用RegExp对象表示,有两种写法:一种是字面量写法,一种是构造函数写法。
一、定义正则表达式
字面量创建方式 /正则表达式/[修饰符可写可不写]
实例创建方式 new RegExp(字符串,[修饰符])
二者的区别:直接量定义不能进行字符串拼接,实例创建方式可以;字面量创建方式特殊含义的字符不需要转义,实例创建方式需要转义。
修饰符(放在第二个斜杠的后面)
g: 代表全局(global)匹配,模式将用用所有字符串,并非在发现第一个匹配项时就停止
i: 不区分大小写,在确定匹配项的时候忽略大小写
m: 多行模式,在到达一行文本末尾还会继续查找下一行中是否存在与模式匹配的项
<script> str = 'davina'; var re = /i/; var re1 = new RegExp('davina'); str1 = 'davi/na'; var re2 = '/i\//'; //注意:正则在匹配/时会有问题,需要转义 console.log(re, re1, re2); // /i/ /davina/ "/i//" </script>
二、常用的正则字符
<script> 转义字符 1. \\ 反斜杠 2. \' 单引号 3. \" 双引号 4. \d 数字(0-9之间的任意一个数字) 5. \D 除数字以外的任意字符 6. \r 回车 7. \n 换行 8. \f 走纸换页 9. \t 横向跳格 10.\s 空格 11.\S 非空格 12.\w 数字,字母,下划线0-9 a-z A-Z 13.\W 非数字,非字母,非下划线 14.\b 单词的边界,独立的部分(字符串的开头结尾,空格的两边都是边界) 15. \B 非边界的部分 16. . 任意一个字符(代表所有) 17. \. 真正的点 中括号 18.[abc] 查找中括号中的任意字符 19.[^abc] 查找任何不在方括号之间的字符(^表示除掉某个字符) 20.[0-9] 查找任何从0-9的数字 21.[a-z] 查找任何从小写a到小写z的字符 22.[A-Z] 查找任何从大写A到大写Z的字符 [\u4e00-\u9fa5] 中文区间,包含所有的汉字 量词:一般用于限制正则的长度 23.{n} 重复n次 (正好n次) 24.{n,m} 至少重复n次,最多重复m次 25.{n,} 至少重复n次,最多不限 (n到多) 26.+ 至少重复1次,最多不限{1,} (1到多) 27.? 至少重复0次,最多重复1次{0,1}(0到1次可有可无) 28.* 至少重复0次,最多不限{0,} (0到多) 29.n$ 匹配任何结尾为n的字符串 30.^n 匹配任何开头为n的字符串 31.x|y 匹配x或者是y </script>
<script> var str = '123456677'; var re = /\d{2}/g; //量词出现元字符后面 var re1 = str.match(re); console.log(re1);['12', '34', '56', '67'] var str = '我是空格'; var reg = /^s+|\s+$/g;//匹配开头结尾空格 var res = str.replace(reg, ''); console.log('(' + res + ')'); // (我是空格) //一般[]中的字符没有特殊的含义,如+就是+,但是像\w这样还是有含义的,[]中一般不会出现两位数字 var str1 = 'abc'; var str2 = 'dbc'; var str3 = '.bc'; var re = /[ab.]bc/; //此时的.就表示.的意思 console.log(re.test(str1), re.test(str2), re.test(str3)); //true false true </script>
三、括号
括号有两个作用分别是分组和引用。具体来说用于限定量词或者选择项的作用范围,也可以用于捕获文本并进行引用或反向引用。
1、分组
量词控制之前元素的出现次数,这个元素可以是字符也可以是表达式,如果把一个表达式用括号括起来那么,这个表达式可以看成是子项(子表达式)。
如下图:这里的/\d{2}/就表示一个分组,匹配两位数字;如果希望字符串’ab‘重复出现2次应该加上括号(ab){2}而不是ab{2}
<script> var re = /\d{2}/; console.log(re.test('12')); //true var re1 = /(ab){2}/; console.log(re1.test('abab'));//true var re2 = /ab{2}/; console.log(re2.test('abab'));//false </script>
2、捕获
括号不仅可以对元素进行分组,还会保存每个分组匹配的文本,等到匹配完成后,引用捕获的内容,因为捕获了文本,这种功能叫捕获分组。
如要匹配2019-12-04这样的日期正则为:/(\d{4})-(\d{2})-(\d{2})/。从左到右第一个括号,第二个括号,第三个括号分别代表1,2,3个捕获。
exec()方法中就用到了捕获,在返回的数组中,第一项是与整个要求匹配的字符串,其它项是与模式中的捕获组匹配的字符串
捕获分组捕获的文本不仅可以用于数据提取,也可以用于替换。在replace()方法中引用分组的形式是$num,num是对应分组的编号
正则具有贪婪性和懒惰性。所谓的贪婪性就是正则在捕获时,每一次都会尽可能多的去捕获符合条件的内容。懒惰性指的是正则在成功捕获一次后不管后边的字符串有没有符合条件的都不再捕获。
<script> console.log(/(\d{4})-(\d{2})-(\d{2})/.test('2019-12-04'));//true console.log(RegExp.$1);//'2019' console.log(RegExp.$2);//'12' console.log(RegExp.$3);//'04' console.log(/(\d{4})-(\d{2})-(\d{2})/.exec('2019-12-04')); //["2019-12-04", "2019", "12", "04", index: 0, input: "2019-12-04", groups: undefined] //把2019-01-01的形式变成01-01-2019 console.log('2019-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/g, '$3-$2-$1')); //01-01-2019 </script>
3、反向引用
正则表达式中也能进行引用,这称之为反向引用。反向引用允许在正则表达式内部引用之前捕获分组匹配的文本,形式是\num,num表示引用分组的编号
<script> var re = /(\w{3}) is \1/ console.log(re.test('kid is kid')); //true console.log(re.test('tom is tom')); //true var re1 = /([a-z])\1/; console.log(re1.test('aa')) //true console.log(re1.test('abc'))//false </script>
4、非捕获
有时候只是为了分组并不需要捕获的情况下就可以用到非捕获分组。以(?:)的形式表示,它只用于限定作用范围,不捕获任何文本。它不能使用反向引用,捕获和非捕获都可以在一个正则表达式中同时出现。
<script> var re = /(?:\d{4})-(\d{2})-(\d{2})/ var date = '2019-12-01'; console.log(re.test(date), RegExp.$1, RegExp.$2) //true "12" "01" console.log(/(\d)(\d)(?:\d)(\d)(\d)/.exec('12345')); //["12345", "1", "2", "4", "5", index: 0, input: "12345", groups: undefined] </script>
5、正向与反向前瞻型分组
正向前瞻型分组:你站在原地往前看,如果前方是指定的东西就返回true,否则为false;
反向前瞻型分组:你站在原地往前看,如果前方不是指定的东西则返回true,如果是则返回false。
<script> var re = /cat is a (?=trouble)/ console.log(re.test('cat is a trouble'), re.test('cat is a brouble')); //true false var re1 = /cat is a (?!trouble)/ console.log(re1.test('cat is a trouble'), re1.test('cat is a brouble')); //false true //前瞻分组和非捕获型分组都不会捕获,区别在于:非捕获型分组匹配到的字符串任然会被外层分组匹配到,而前瞻型不会 var re, str = 'cat is a trouble'; re = /(cat is a (?:trouble))/; console.log(re.test(str), RegExp.$1) //true "cat is a trouble" re = /(cat is a (?=trouble))/ console.log(re.test(str), RegExp.$1) //true "cat is a " </script>
四、断言
在正则表达式中,有些结构并不是真正匹配文本,而只负责判断在某个位置左/右是否符合要求,这种结构被称为断言,也可称为锚点。强调:断言只是条件,帮助找到真正需要的字符串,本身是不会匹配的。
1、单词边界
单词边界是指单词字符(\w)能匹配的字符串的左右位置。在java和js中,单词字符(\w)等同于[0-9a-zA-Z]。所以在这些语言中可以用\b\w+\b把所有的单词提取出来。
单词边界是\b(它其实是一个位置匹配符),它能匹配的位置:一边是单词字符\w,一边是非单词字符\W.(在\w和\W之间)。
与\b对应的还有\B,表示非单词的边界这个用的很少
<script> var str = 'abc_123,4java=efg汉字'; var reg = /[\d\D]\b/g;["4", ",", "a", "=", "g"] console.log(str.match(reg)); var str1 = 'Wrong cannot afford defeat but right can'; var reg1 = /\b\w+\b/g; console.log(str1.match(reg1)); //["Wrong", "cannot", "afford", "defeat", "but", "right", "can"] //如有特殊的单词如e-mail上面的正则就无法匹配要进行修改 </script>
2、^和$
常用的断言还有^和$,它们分别是匹配字符串的开始和结束位置,可以用来判断整个字符串能否由表达式匹配。^和$常用功能是删除字符串首尾多余的空白。
<script> var str = 'first word\nsecond word\nthird word'; reg = /^\w*/ console.log(reg.exec(str)); /["first"]/ function fn(str) { reg1 = /^\s+|\s+$/ return str.replace(reg1, '') } console.log(fn(' davina ')); //davina </script>
3、环视
环视是指在某个位置向左向右看,保证其左右位置必须出一某类字符。且环视也中同上面两个断言一样只是做判断。它在匹配过程中,不占用字符,所以被称为"零宽"。
零宽度正先行断言
(?=exp):字符出现的位置的右边必须匹配到exp这个表达式
<script> var str = 'abcdefg'; re = /.*(?=d)/; console.log(re.exec(str)); //["abc"] var str1 = 'davina6666'; var str2 = 'davina22222'; var reg = /davina(?=6666|4444)/; console.log(str1.match(reg), str2.match(reg));//["davina"] null </script>
零宽度负先行断言
(?!exp) : 字符出现的位置的右边不能是exp这个表达式
<script> var str = "#www#eee"; var reg = /(?!\#)\w+/g; console.log(str.match(reg)); //["www", "eee"] </script>
零宽度正后发断言
(?<=exp):字符出现的位置的左边是exp这个表达式(js不支持)
零宽度负后发断言
(?<!exp):字符出现的位置的左边不能是exp这个表达式(js不支持)
五、正则相关方法
1、RegExp对象相关的方法
test()方法
作用:查看正则表达式与指定字符串是否匹配,用的最多是来做判断的
语法:正则.test(字符串);
返回值:返回true/false
exec()方法
作用:返回匹配的结果,与match类似
语法:正则.exec(字符串)
返回值:数组或者是null
<script> var str = '123dvain4553a'; var re = /a/; console.log(re.test(str)); //true var re1 = /\d/; if (re1.test(str)) { console.log('有数字') //有数字 } else { console.log('没有数字') } var str2 = 'cat,dog,fish,chicken,duck,bat'; var re2 = /.at/; var re3 = /da/; console.log(re2.exec(str2));//["cat", index: 0, input: "cat,dog,fish,chicken,duck,bat", groups: undefined] console.log(re3.exec(str2)); //null </script>
2、String对象相关方法
match()方法
作用:匹配指定的字符串或者是正则,把匹配到的结果放到一个数组中
语法:字符串.match(字符串或者正则)
返回值:找到后把结果放在数组中且返回,没有找到返回null
注意:如果不带g修饰符,只会匹配一个结果,且给找到的数组增加两个属性:index(找到字符对应的位置),input(原字符串,从哪个字符串身上找)。
exec()和match()的区别:1. 一个是正则一个是String. 2. exec()只会匹配第一个符合的字符串(g对其并不起作用),match是否返回所有匹配的数组和正则里是否带g有关。
<script> var str = 'dav2in34n345av'; var re = /v/; var re1 = /V/; console.log(str.match(re), str.match(re1)); //["v", index: 2, input: "dav2in34n345a", groups: undefined] null var re2 = /v/g; var re3 = /\d+/g; //有量词+,所以找到的字符数量不是单个的了 console.log(str.match(re2)); // ["v", "v"] console.log(str.match(re3));// ["2", "34", "345"] var str1 = 'dav2in34nda345dav'; var re4 = new RegExp('da', 'g'); var re5 = /da/g; console.log(re4.exec(str1)); //['da'] console.log(str1.match(re5)); //['da','da','da'] </script>
replace()方法
作用:替换匹配的字符串
语法:字符串.replace(字符串或者正则,字符串或者函数)
参数:在这里参数要说明一下:参数1是字符串或者正则,要匹配的内容,与replace的第一个参数一样;参数2是要匹配的内容对应的位置下标;参数3是原字符串。
要强调的是这个函数一定要一个返回值,否则会用undefined来替代
返回值:替换后的新字符串,原来的字符串没有任何的变化
<script> var str = 'davinadavina'; var re = /d/g; console.log(str.replace('d', 'h'), str); //havinadavina davinadavina console.log(str.replace(re, 'h'), str); //havinahavina davinadavina var newStr = str.replace('d', function () { return 'f'; }) console.log(newStr); //favinadavina //1. 把da全部换成*号 var re1 = /da/g; var newStr1 = str.replace(re1, function ($0, $1, $2) { console.log($0, $1, $2); //da 6 davinadavina var str = ''; for (var i = 0; i < $0.length; i++) { str += '*'; } return str; }) console.log(newStr1) //**vina**vina //2. 去掉字符串两边的空格,包括里面的空格 var str = ' davina davina davina '; var str0 = str.replace(/\s+/g, ''); //davinadavinadavina console.log(str0); //去掉左空格 var str1 = str.replace(/^\s*/, '');//davina davina davina console.log(str1); //去掉右空格 var str2 = str.replace(/(\s*$)/g, ''); // davina davina davina console.log(str2); //去掉左右空格 var str3 = str.replace(/(^\s+)|(\s+&)/g, ''); //davina davina davina console.log(str3); // ^s+表示以空格开头的连续空白字符,s+$表示以空格结尾的连续空白字符 </script>
闪亮字体效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> span { transition: 0.5s all ease; } </style> </head> <body> <div id="div"> 你微笑地看着我,不说一句话。而我知道,为了这个,我已经等了很久了。 有一次,我们梦见大家都是不相识的。我们醒了,却知道我们原是相亲相爱的。 有一天,我们梦见我们相亲相爱了,我醒了,才知道我们早已经是陌路。 世界上最遥远的距离,不是生与死,而是我就站在你面前,你却不知道我爱你。 </div> <script> var val = div.innerHTML; div.innerHTML = val.replace(/./g, function (x) { return '<span>' + x + '</span>' }) setInterval(() => { var span = document.getElementsByTagName('span'); for (var i = 0; i < span.length; i++) { span[i].style.color = 'rgb(' + parseInt(Math.random() * 256) + ',' + parseInt(Math.random() * 256) + ',' + parseInt(Math.random() * 256) + ')'; } }, 500); </script> </body> </html>
改变日期格式
<script> console.log(/(\d{4})-(\d{2})-(\d{2})/.test('2019-12-04'));//true console.log(RegExp.$1);//'2019' console.log(RegExp.$2);//'12' console.log(RegExp.$3);//'04' console.log(/(\d{4})-(\d{2})-(\d{2})/.exec('2019-12-04')); //["2019-12-04", "2019", "12", "04", index: 0, input: "2019-12-04", groups: undefined] //把2019-01-01的形式变成01-01-2019 console.log('2019-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/g, '$3-$2-$1')); //01-01-2019 </script>
search()方法
作用:找到匹配的字符串首次出现的位置
语法:字符串.search(字符串或者正则)
返回值:找到了返回位置的下标,没有找到返回-1
注意:search()方法的参数可以是正则,indexOf()的方法参数不可能是正则
<script> var str = 'davina123Davian'; console.log(str.indexOf('v')); //2 console.log(str.search('v'));//2 var re = /\d/; console.log(str.indexOf(re), str.search(re));//-1 6 </script>
split()方法(后面有详细介绍)
作用:按约定字符串或者字符串规则拆分成数组,接受一个字符串或者正则
语法:字符串.split(字符串或者正则)
返回值:数组
<script> var color = 'red,green,blue,white,yellow'; var color1 = color.split(/[^\,]+/); console.log(color.split(',')); //["red", "green", "blue", "white", "yellow"] console.log(color1) // ["", ",", ",", ",", ",", ""] </script>
六、常用的正则
<script> //常用的正则 //1、手机号 var reg = /^1[34578]\d{9}$/; console.log(reg.test('13733475019')); //true //2、身份证号 var reg1 = /^[1-9]\d{5}(18|19|20|(3\d))\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/; console.log(reg1.test('53010219940508011X')); //3、qq号 var reg2 = /^[1-9]\d{4,11}$/; console.log(reg2.test('2694511934')); //true //4、包含中文正则 var reg3 = /[\u4E00-\u9FA5]/; console.log(reg3.test('武汉')); //true //5、密码 //要求:长度在6-16,可包含大小写字母,可包含数字,下划线和减号 var reg4 = /^[\w_-]{6,16}$/; console.log(reg4.test('234sffr_')); //true //要求:长度在6-16,须包含1个数字,1个小写字母,大写字母,其它随意 var reg5 = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{6,16}$/ console.log(reg5.test('34s43F83C')); //要求:长度6-16,至少一个大写字母,1个小写字母,1个数字,1个特殊字符 var reg6 = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{6,16}$/; console.log(reg6.test('13rsffdDE%')); //true //6、邮箱 var reg7 = /^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/; console.log(reg7.test('2345@163.com')); //true //7、用户名正则 //要求:4-16位(数字,字母,中文,下划线,减号) var reg8 = /^[\u4E00-\u9FA50-9a-zA-Z_-]{4,16}$/ console.log(reg8.test('davina234-')); //true </script>