一、什么是正则表达式
正则表达式就是可以验证字符串是否符合某个规则(test),也可以把字符串中符合规则的内容捕捉到(exec/match…)
正则表达式就是一个表达式,用来查找一个有指定【特点】的表达式,规则匹配的。
正则:就是一个规则,就是用来处理字符串的一个规则
- 匹配 :判断一个字符串是否符合我们指定的的规则 -> test , 语法:reg.test(str)
- 捕获 :把字符串汇总符合我们正则规则的内容捕获到 ->exec , reg.exec(str)
https://regexr-cn.com/
二、正则表达式的两种创建方式
正则表达式的组成
每一个正则表达式都是由元字符 和 修饰符组成的
【元字符】: 在/ /之间具有一些意义的字符
: 转义字符,转义后面字符所代表的含义
1. 字面量创建方式
两个斜杠之间包起来的,都是用来描述规则的元字符
let reg = /\d+/; //=>\d代表0-9之间的数字
2. 基于构造函数模式创建
有两个参数:1.元字符字符串;2.修饰符字符串(什么是元字符和修饰符后面会解释)
let reg1 = new RegExp(’\d+’); //=>/\d+/;
let reg2 = new RegExp(’\d+’,‘g’) //=>/\d+/g
RegExp 是javascript中的一个内置对象。为正则表达式。
\d 在RegExp实例化的时候识别不了, 需要转义
3. 字面量方式和实例创建的方式在正则中的区别:
有个小知识点需要注意下:“\”在字符串中也有特殊含义,表示转义字符
//在字面量方式中,我们/ /之间包起来的所有的内容都是元字符,有的具有特殊的意义,大部分都是代表本身含义的普通的元字符
//需求:name是一个变量, 写一个正则, 让前面有数字开头, 后面是数字结尾
var name = 'ruanmou';
var reg = /^\d+"+name+"\d+/; // 可以吗, 不可以
console.log(reg.test('2015ruanmou2016')) //=>false
console.log(reg.test('2015""namee"2016')) //=>true
// 所以对于这样的需求,我们只能使用实例创建的方式了
var name = 'ruanmou';
var reg=new RegExp("^\\d+"+name+"\\d+$", "g") ;
console.log(reg.test('2015ruanmou2016')) //=>true
总结:
1> 字面量方式中出现的一切都是元字符,所以不能进行变量值的拼接,而实例创建的方式是可以的
2> 字而量方式中直接写\d就可以,而在实例中需要把它转译成\d
三、正则表达式使用方法
reg.test(str); 匹配 : 判断一个字符串是否符合我们指定的的规则
reg.exec(str) 捕获 :把字符串汇总符合我们正则规则的内容捕获到
查看RegExp构造器里的原型方法
RegExp.prototype
str.match(reg);
四、元字符
1. 常用元字符
. 查找单个字符,除了换行(\n)和行结束符。
\w 查找单词字符(匹配字母、数字、下划线。等价于’[A-Za-z0-9_]’) [(A-Z)(a-z)(0-9)(_)]
\W 查找非单词字符
\d 查找数字
\D 查找非数字
\s 查找空白字符
\S 查找非空白字符
\b 查找单词边界
\B 查找非单词边界
\n 查找换行符 , 后者使光标下移一行
\f 查找换页符
\r 查找回车符,使光光标到行首
\t 查找制表符
\v 查找垂直制表符
\uxxx 查找以十六位进制数xxxx规定的Unicode字符
[\u4e00-\u9fa5] 所有中文字符
2. 元字符反义
反义,匹配不满足某个条件的字符串
\W 查找非单词字符,匹配任意不是字母,数字,下划线的字符
\D 查找任意非数字的字符
\S 查找非空白字符
\B 查找非单词边界,匹配任意的非单次开头或者结尾的位置
[^abc] 匹配不是a,b,c的任意字符
3. 量词元字符:
代表出现次数的量词元字符
以下均遵循贪婪匹配:
n+ 匹配任何包含至少一个n的字符
n* 匹配任何包含0个或多个n的字符
n? 匹配任何0个或1个n的字符
n{X} 匹配X个n的序列字符串
n{X,Y} 匹配X至Y个n的序列字符串
n$ 匹配以n结尾的字符串
^n 匹配任何以n开头的字符串
?=n 正向肯定预查匹配任何后面紧接着指定字符串n的字符串
?!n 正向否定预查匹配任何其后没有紧接着指定字符串n的字符串
?<=n 反向肯定预查匹配任何前面紧跟着指定字符串n的字符串
?<!n 反向否定预查匹配任何前面没有紧接着指定字符串n的字符串非贪婪匹配:n+? n*? …
4.表示边界
^ 匹配字符串开头, 如果出现在中括号, 表示 排除 , 非
$ 匹配字符串结尾
\b 匹配一个单词的边界
\B 匹配非单词边界
\b\w{6}\b 匹配刚好6个字符的单词。
reg = /\b\w{6}\b/g;
reg.test('1234566 ffffff') //=>false
reg.test('1234566 ffffff ') //=>true
元字符^(和数字6在同一个键位上的符号)和$都匹配一个位置,这和\b有点类似。
^匹配你要用来查找的字符串的开头,$匹配结尾。
这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$。
- ^只有在[]中的最开始才有特殊含义,表示排除,否则也是表示它的字面意思。比如
[^0-9]匹配不包含0-9的任意字符。
如果^在[]中的其他位置,就会只表示它的字面意思了。
比较常见的错误是[http|https],我们的本意是要匹配http或者https中的任意一个,但其实这个正则表达式和[ht|ps]是一模一样的,表示匹配h,t,p,|,s中的任意一个。
要达到上面的效果,我们可以这样写(http|https),或者https?。
^ $ 是不会占位的
5、特殊字符
具有特殊含义的字符。
\ 转义字符,转义后面字符所代表的含义
| 或 ,分支条件
-
因为\是用来转义的,如果我们不对\转义,它就会对别人转义,所以如果要在[]中匹配\本身,我们需要使用\
-
分支条件| ,使用分支条件一定要使用()括起来,而且分支是短路的,只要符合其中的一个,就不会继续匹配分支中的其他条件了。
-
当我们使用|的时候,一定要使用()进行限定 , |的匹配范围是从左到右,直到结束或者遇到)为止
五、常用修饰符 i/g/m
g代表的是global全局匹配
i代表ignoreCase忽略大小写,
m代表multiline多行匹配,
六、方括号
匹配[]中的任意一个,单字符匹配
[abc] 查找方括号内的任意一个字符
[^abc] 查找任何不在方括号内的字符,^在中括号时,读非,除了abc三个字符意外的任何字符
[0-9] 查找0-9之间的数字
[a-z] 查找任何小写字母
[A-Z] 查找任何大写字母
[A-z] 查找任何字母
[^a-z] 除了a-z之间的任何一个字符
- 在[]中用来代表一个范围,比如[0-9],[a-z],当-挨着[或者]的时候,-表示它的字面意思,比如
[-abc]会匹配-,a,b,c中的任意一个
[a-c]会匹配a b c中的任意一个
[a\-c]会匹配a,-,c这三个中的任意一个
八、分组()
把一个大正则划分成几个小的正则;
()小分组的作用
1.提升优先级
2.分组引用
3.分组捕获
(red|blue|green) 查找指定字符串(子表达式)
默认会给每一个分组一个分组号,从1开始分配,0代表整个正则表达式,我们可以在稍后通过分组号来引用前面的分组,比如\1代表引用第一个分组匹配到的内容,\n代表引用第n个分组匹配到的内容
举几个例子加深印象
\b(\w+)\b\s+\1\b,可以匹配任意的重复单次,比如go go,good good等。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字\b(\w+)\b,这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符\s+,最后是分组1中捕获的内容(也就是前面匹配的那个单词)\1。
其中\1就是引用的第一个分组匹配到的内容。
1. 分组引用
let str = "book";
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/;
// => 分组引用就是通过“\数字”让其代表和对应分组出现一摸一样的内容
console.log(reg.test("look")); // true
console.log(reg.test("deep")); // true
console.log(reg.test("book")); // true
console.log(reg.test("sonn")); // false
2. 分组捕获
先总后分 先左后右 先外后里
let str = 'zhu2019zhu2020zhu2021',
reg = /\d{1,3}?/;
console.log(reg.exec(str));
reg = /(([a-z])+)((\d)+)/g;
这种括号套括号的情况,最小的分组永远捕获到的是符合条件的最后一项,也可以理解为把量词放在小括号外的,小分组捕获到的永远是整个分组的最后一项
console.log(reg.exec(str));
//=>[“zhu2019”, “zhu”, “u”, “2019”, “9”, index: 0, input: “zhu2019zhu2020zhu2021”, groups: undefined]
// RegExp.$1是RegExp的一个属性,指的是与正则表达式匹配的第一个子匹配(以括号为标志)字符串,以此类推,RegExp.$2,RegExp.$3,..RegExp.$99总共可以有99个匹配
RegExp.$1~RegExp.$9:获取当前本次正则匹配后,第一个到第九个分组的信息
例子:
var r= /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
//正则表达式 匹配出生日期(简单匹配)
r.exec('1985-10-15');
s1=RegExp.$1;
s2=RegExp.$2;
s3=RegExp.$3;
alert(s1+" "+s2+" "+s3)//结果为1985 10 15
九、?五大作用
- ?左边是非量词元字符:本身代表量词元字符。出现零到一次
- 问号左边是量词元字符:取消捕获时候的贪婪性
- (?: ) 只匹配不捕获
- (?=) 正向预查
- (?!) 负向预查
// 正负向预查
let reg = /ruanmou/;
reg.test('ruanmousss') //=>true
// 正向预查
let reg1 = /ruanmou(?=peixun)/ // ruanmou后面必须跟培训
reg1.test('ruanmousss') //=>false
reg1.test('ruanmoupeixun') //=>true
reg1.test('ruanmoupeixunssss') //=>true
reg1.test('ruanmoussspeixunssss') //=>false
// 负向预查
let reg = /ruanmou(?!peixun)/; //ruanmou后面必须不跟peixun
console.log(reg.test('ruanmoupeixun')) // false
console.log(reg.test('ruanmoupei')) // true
断言
断言和\b,^,$一样,匹配一个位置,这个位置满足一定的条件。
-
正预测断言 , 正向预查
(?=expr)断言一个位置,这个位置后面满足expr。比如\b\w+(?=ing\b),这个正则表达式匹配以ing结尾的单词的前半部分,比如dacing,会匹配到dac. 这个正则表达式的意思是,首先是一个单词,这个单次后面跟着ing -
负预测断言, 负向预查
(?!expr)断言一个位置,这个位置后面不满足expr,。主要用去确保被匹配的字符串中不会出现一些字符,比如我们想要匹配这样一个字符串,它以hello开头,但是没有出现good,我们可以这样写
\bhello.(?!good)\w\b
可以看一下很实用的例子
(?<=<(\w+)>).*(?=</\1>),这个可以用来匹配HTML标签中的内容,具体如下,首先是一个断言(?<=<(\w+)>),断言一个位置,这个位置前面是类似这样的标签,然后是任意的字符,接下来又是一个断言(?=</\1>),断言这个位置后面是能匹配到前面那样的标签内容,只是多了一个反斜杠,比如<\body>
十、贪婪性或懒惰
默认正则表达式是贪婪的,会尽可能多的匹配,比如字符串aabab,如果使用正则表达式a.*b去匹配的话,会匹配到整个字符串。
有时候我们需要懒惰匹配,要尽可能少的匹配,这时候我们就需要在前面提到的用于重复的元字符后面加一个?,比如*? , +? , ?? , {n,m}? , {n,}?
举例说明
(?=n)
var str="hello world,all students";
var patt1=/ll(?=o)/g;
var patt2=/ll/g;
str.match(patt1) //["ll"]
str.match(patt2) //["ll","ll"]
总结: (?=o) 起到了 辅助定位作用…
var str = "12345678";
var patt = /\B(?=(...)+$)/g;
console.log(str.replace(patt,","));
// 12,345,678
// https://i.getshell.cn/2018/03/15/js-%E6%AD%A3%E5%88%99%E9%87%8F%E8%AF%8D-n%E7%94%A8%E6%B3%95/
默认情况下正则捕获的时候,是按照当前正则所匹配的最长结果来获取的
解决贪婪性:把后面的量词元字符去掉或者在量词元字符后面加?;
let str = 'sss2010ddd1855';
let reg = /\d+/;
console.log(reg.exec(str)); //=>["2010", index: 3, input: "sss2010ddd1855", groups: undefined] 2010中2,20,201都符合/\d+/这个条件但是却匹配到了2010,这就是正则匹配的贪婪性
// 解决方案一
reg = /\d/
console.log(reg.exec(str)); //=>["2", index: 3, input: "sss2010ddd1855", groups: undefined];
// 解决方法二
reg = /\d+?/g
conosle.log(reg.exec(str)); //=>["2", index: 3, input: "sss2010ddd1855", groups: undefined]
正则捕获的懒惰性
每执行一次 exec 只能捕获到一个符合正则规则的,但是默认情况下执行多少次,获取的结果永远都是第一个匹配到的,其余的捕获不到,即正则捕获的懒惰性:默认只捕获一个
- 懒惰性的原因:默认情况下 lastIndex 的值不会被修改,每一次都是从字符串开始位置找,所以找到的永远是第一个符合规则的(任何捕获方法都是这样)
- 解决正则的懒惰性用全局匹配 g
RegExp对象属性
global 查看RegExp对象是否具有g标志
ignoreCase 查看RegExp对象是否具有i标志
multiline 查看RegExp对象是否具有m标志
source 查看正则表达式源文本
lastIndex 一个整数标志开始下一次匹配的字符位置(一般与exec一起使用)
var str = 'The best things in life are fre';
var pat1=new RegExp("be");
pat1.global //false
pat1.ignoreCase //false
pat1.multiline //false
pat1.source //be
pat1.lastIndex //0
十一、RegExp对象方法
正则 RegExp.prototype 上的方法
compile 编译正则表达式。 (就是改变这个正则表达式)
exec 检索字符串中指定的值。返回找到的值,并确定其位置。
test 检索字符串中指定的值。返回 true 或 false。
compile() 方法用于改变 RegExp。
compile() 既可以改变检索模式,也可以添加或删除第二个参数。
var str = 'The best things in life are fre';
var pat1=new RegExp("be");
//更换前
var t1 = pat1.exec(str)
console.log(t1)
pat1.compile("ng");
//更换后
var t2 =pat1.exec(str)
console.log(t2)
1. test 方法
返回 Boolean,查找对应的字符串中是否存在模式
var str = "1a1b1c";
var reg = new RegExp("1.", ""); //.代表任意一个字符
reg.test(str) // true
var str = "hello world!";
var result = /^hello/.test(str);
console.log(result); // true
test()继承正则表达式的lastIndex属性,表达式在匹配全局标志g的时候须注意。
function testDemo(){
var r, re; // 声明变量。
var s = "I";
re = /I/ig; // 创建正则表达式模式。
document.write(re.test(s) + "<br/>"); // 返回 Boolean 结果。
document.write(re.test(s) + "<br/>");
document.write(re.test(s));
}
testDemo();
输出结果:
true
false
true
当第二次调用test()的时候,lastIndex指向下一次匹配所在位置1,所以第二次匹配不成功,lastIndex重新指向0,等于第三次又重新匹配。
2. exec 方法
exec 查找并返回当前的匹配结果,并以数组的形式返回。
rgExp.exec(str)
参数
- rgExp
必选项。包含正则表达式模式和可用标志的正则表达式对象。 - str
必选项。要在其中执行查找的 String 对象或字符串文字。 - 返回数组包含:
input:整个被查找的字符串的值;
index:匹配结果所在的位置(位);
arr:结果值,arr[0]全匹配结果,arr[1,2…]为表达式内()的子匹配,由左至右为1,2…。
注意:在匹配后,rgExp 的 lastIndex 属性被设置为匹配文本的最后一个字符的下一个位置。lastIndex并不在返回对象的属性中,而是正则表达式对象的属性。
例子1:不含子表达式的正则表达式exec方法循环应用
!function RegExpTest(){
var src="http://ruanmou.blog.163.com/blog/I love you!";
// 注意g将全文匹配,不加将永远只返回第一个匹配。
var re = /\w+/g;
var arr;
// exec使arr返回匹配的第一个,while循环一次将使re在g作用寻找下一个匹配。
while((arr = re.exec(src)) !=null){
document.write(arr.index + "-" + re.lastIndex + ":" + arr + "<br/>");
for(key in arr){
document.write(key + "=>" + arr[key] + "<br/>");
}
document.write("<br/>");
}
}()
exec默认只返回匹配结果的第一个值,比如上例如果不用while循环,将只返回’http’(尽管后面的ruanmou等都符合表达式),无论re表达式用不用全局标记g。但是如果为正则表达式设置了全局标记g,exec从上次匹配结束的位置开始查找。如果没有设置全局标志,exec依然从字符串的起始位置开始搜索。利用这个特点可以反复调用exec遍历所有匹配,等价于match具有g标志。当然,如果正则表达式忘记用g,而又用循环(比如:while、for等),exec将每次都循环第一个,造成死循环。
如果正则表达式中包含子表达式,那么输出结果将包含子匹配项
例子2:包含子表达式的正则表达式exec方法应用
!function execDemo(){
var r, re; // 声明变量。
var s = "The rain in Spain falls mainly in the plain";
re = /[\w]*(ai)n/ig;
r = re.exec(s);
document.write(r + "<br/>");
for(key in r){
document.write(key + "-" + r[key] + "<br/>");
}
}()
十二、字符串方法
search 检索正则表达式相匹配的值
match 查找所有符合正则匹配条件的结果
replace 替换与正则表达式匹配的字符串
split 把字符串分割成数组(注: 用字表达式分割的话会保留子表达式)
1. replace
基于replace捕获:即把捕获到内容进行替换,第二个参数可以传递回调函函数,可以把正则表达式每次捕获到的内容传递给回调函数,进行相应的处理。然后把回调函数每次执行的返回值拿来替换捕获到的内容。
let time = '123-456-789';
time = time.replace(/-/g,'');
console.log(time) //=> '123456789'
2. match
使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回。
stringObj.match(rgExp)
@params:正则对象(如果参数是非正则表达式对象,会默认 new RegExp(obj))
@return:数组(如果参数为空返回[’’];)
@如果传的正则表达式是全局捕获,则返回大正则每次匹配的内容,不会返回小分组匹配的内容
@如果不是全局捕获,返回第一次与大正则匹配的内容及其分组匹配的内容
let str = 'asc1021dad201';
str.match(/\d+/); //=>["1021", index: 3, input: "asc1021dad201", groups: undefined]
let str = 'asc1021dad201';
str.match(/\d+/g); //=>["1021", "201"]
// match多次匹配的情况下,match只能把大正则匹配的内容捕获到,小分组匹配的信息无法获取
let str = 'abc123adc456';
str.match(/[a-z](\d+)/g); //=>["c123", "c456"]
正则表达式中的分组捕获
// => 身份证号码
let str = "620421196608034816";
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;
console.log(reg.exec(str)); // => ["620421199410102456", "620421", "1994", "10", "10", "5", index: 0, input: "620421199410102456", groups: undefined]
console.log(str.match(reg)); // => ["620421199410102456", "620421", "1994", "10", "10", "5", index: 0, input: "620421199410102456", groups: undefined]
// =>第一项:大正则匹配结果
// =>其余项:每一个小分组单独匹配捕获的结果
// =>如果设置了分组(改变优先级),但是捕获的时候不需要单独捕获,可以基于?:来处理
!function MatchDemo(){
// 声明变量。
var r, re;
var s = "The rain in Spain falls mainly in the plain";
// 创建正则表达式模式。
re = /(a)in/ig;
// 尝试去匹配搜索字符串。
r = s.match(re);
// 返回的数组包含了所有 "ain" 出现的四个匹配,r[0]、r[1]、r[2]、r[3]。
//但没有子匹配项a。
document.write(r);
}()
输出结果:ain,ain,ain,ain
match() 方法将检索字符串 stringObject,以找到一个或多个与 regexp 匹配的文本。这个方法的行为在很大程度上有赖于 regexp 是否具有标志 g。
3. split
@params:正则对象
@return:基于正则捕获到的所有字符分割后的数组
// 基于split进行捕获:即可以选定多个字符进行替换
let str = 'dadw#frr%dd#frgg%3223s';
let ary = str.split(/[#%]/g);
console.log(ary); //=>["dadw", "frr", "dd", "frgg", "3223s"];
4. search
@params:正则对象,如果传入的是一个非正则表达式对象 obj,则会隐式的基于 new RegExp(obj)进行转换
@return:第一次匹配成功对应的索引,否则返回-1;
// 基于search捕获
let str = "dadA1452";
console.log(str.search(/[^a-zA-Z]/g)); //=>4
相关面试题
1.正则表达式实现aabb的形式变成bbaa
2.给10000000000三位打点 变成 10.000.000.000
3.字符串去重 aaaaaaaaaaaaaaaaaaaaaabbbbbbbbcccccccccc变成abc
4.把the-first-name转换成小驼峰式theFirstName
答案:
1.正则表达式实现aabb的形式变成bbaa
var reg = /(\w)\1(\w)\2/g;
方法一:
var str = “aabb”;
str.replace(reg,"$2$2$1
1
"
)
;
/
/
1"); //
1");//可以引用reg中的子表达式的内容 KaTeX parse error: Undefined control sequence: \w at position 20: …一个 var reg = /(\̲w̲)\1(\w)\2/g; 方法…,$1,KaTeX parse error: Expected '}', got 'EOF' at end of input: 2){ // 正则表达式匹配的全局结果“aabb”
$1表示第一个子表达式匹配的内容
$2表示第二个子表达式匹配的内容
return $2+$2+$1+$1;
});
2. 将数值变成英文计数式:10000000—>100.000.000
var reg = /(?=(\B)(\d{3})+$)/g;
var str = “100000000”;
str.replace(reg,".");
3.字符串去重 aaaaaaaaaaaaaaaaaaaaaabbbbbbbbcccccccccc变成abc
var str = ‘aaaaaaaaabbbbbccc’;
var reg = /(\w)\1*/g;
str.replace(reg,"$1"); //“abc”
4.把the-first-name转换成小驼峰式theFirstName
<!--
正则表达式的用途: 过滤字符串的(找出符合我们要求的字符串)
希望让the-first-name这个字符串 转换成小驼峰形式 theFirstName
解析: 1. 找到特定的子字符串 (-f, -n)
2. 替换字符串
子表达式的作用: 拿取正则表达式匹配出来的子字符串中的子字符串
-->
var reg = /-(([a-z]))/g;
var str = “the-first-name”;
var newStr = str.replace(reg, function ($, $1, $2, $3) {
return $1.toUpperCase();
})
邮箱
验证邮箱的正则(简版)
//左边:数字、字母、下环线、…-
//11447092650.qq.com
//peixun@ruanmou.cn
//laney@163.com
var reg = /1+@[0-9a-zA-Z]+(.[a-zA-Z]{2,4}){1,2}$/;
reg.test(‘laney@163.com’)
\w_. ↩︎