# 1、正则表达式的常用方法
# 1、test
验证字符串是否符合某个规则。
# 2、exec
用于捕获字符串中的字符
# 2、创建字符串的两种方式
# 1、字面量
正则表达式中的部分内容是变量存储的值。两个斜杆中间包起来的都是元字符。不能在正则表达式中拼接变量。
const reg=/\d+/;
let type="james";
const reg2=/^@"+type+"@$/;//这里的"+表示匹配"一次或多次
console.log(reg2.test("@james@"));//false
const reg3=new Regexp("@"+type+"@");
console.log(reg3.test("@james@"));//true
1
2
3
4
5
6
7
2
3
4
5
6
7
# 2、构造函数
构造函数因为传递的是字符串,""需要写两个斜杆。
//验证数字
const reg1=/\d+/;
const reg1=new Regexp("\\d+");//"\"需要写两个才代表斜杆
1
2
3
2
3
# 3、正则表达式的组成
正则表达式由两部分组成,元字符和修饰符。
# 1、元字符
常用的元字符
# 1、量词元字符:设置出现的次数。
- 零到多次
- 1到多次
- ?0次或者1次
- {n} 出现n次
- {n,} 出现n到多次
- {n,m} 出现 n 到 m次
# 2、特殊元字符:单个或者组合在一起代表特殊额含义
- \ 转义字符(普通->特殊->普通)。
- . 除\n(换行符)以外的任意字符。
- ^ 以哪一个元字符作为开始
- $ 以哪一个元字符作为结束
- \n 换行符
- \d 0~9之间的数字
- \D 非0~9之间的一个数字
- \w 数字、字母、下划线之间的任意一个字符 [a-zA-Z0-9_]
- \s 一个空白字符(包含空格、制表符、换行符)
- \t 制表符(一个TAB键,四个空格)
- \b 匹配一个单词的边界
- x|y x或者y中的一个字符
- [xyz] x或者y或者z中的一个字符
- [^xy] 除了x/y以外的字符
- [a-z] 指定a-z这个范围中的任意字符
- () 正则中的分组
- (?😃 只匹配不捕获
- (?=) 正向预查
- (?!) 负向预查
# 3、普通元字符:代表本身含义
# 4、元字符详解
^.$
let reg1=/^\d/;//以数字开头
let reg2=/\d$/;//以数字结束
let reg3=/^\d+$/;//只能是数字
//验证手机号码
let reg4=/^1\d{10}$/
1
2
3
4
5
2
3
4
5
\
let reg=/^2.3$/;//2和3之间可以是除\n之外的任意字符
let reg2=/^\\d/;//匹配以\d开始的字符串
const reg3=RegExp("\\d");
1
2
3
2
3
x|y
直接x|y
会存在很乱的优先级问题 ,一般写的时候都伴随着小括号进行分组,因为小括号改变处理的优先级。
let reg1=/^(18|29)$/;
console.log(reg.test("18"));//true
console.log(reg.test("29"));//true
console.log(reg.test("128"));//false 不加()时为true
console.log(reg.test("189"));//false 不加()时为true
1
2
3
4
5
6
2
3
4
5
6
[]
括号中出现的都是本身的意义。中括号中不存在多位数。
let reg=/^[@+]$/;
let reg2=/^[\d]$/;//匹配数字
reg2.test("9");//true
let reg3=/^[10-29]$/;//匹配的是1,0-2,9
1
2
3
4
2
3
4
# 5、常用的正则表达式
- 1、验证是否是有效数字
/*
*规则分析
+1、可能出现 + - 号 [+-]
+2、一位数0-9都可以,多位首位不能为0 (\d|([1-9]\d+))
+3、小数部分可能有可能没有,一旦有后面必须有小数点+数字 (\.\d+)?
*/
let reg=/^[+-]?(\d|([1-9]\d+))(\.\d+)$/;
1
2
3
4
5
6
7
2
3
4
5
6
7
- 2、验证密码
//=>数字、字母、下划线
//=>6~16位
let reg=/^\w{6,16}/;
1
2
3
2
3
- 3、验证真实姓名的
/*
+ 1、汉字 /^[\u4E00-\u9FA5]/
+ 2、名字长度 2~10
+ 3、可能有译名 (.汉字)(.[\u4E00-\u9FA5]{2,10}){0,2}
*/
let reg=/^[\u4E00-\u9FA5]{2,10}(.[\u4E00-\u9FA5]{2,10}){0,2}$/;
1
2
3
4
5
6
7
2
3
4
5
6
7
- 4、验证邮箱
//\w+((-\w+)|(\.\w+)) 开头是数字字母或下划线
let reg=/^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+(\.|-)[a-z0-9]+)*\.[A-Za-z0-9]+$/;
1
2
2
- 5、身份证号码
/*
+ 1、一共18位
+ 2、最后一可能是X
+ 3、前面6位是 省市县,中间8位是年月日 最后4位 倒数第二位偶数是女,奇数是男
*/
let reg=/^(\d{6})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|x)$/
reg.exec("430421199408011654");
1
2
3
4
5
6
7
2
3
4
5
6
7
# 2、修饰符
正则表达式常用的修饰符
- i
ignoreCase
忽略单词大小写匹配 - g
global
全局匹配 - m
multiline
可以进行多行匹配
# 4、正则捕获
实现正则捕获的方法。实现正则捕获的前提是:当前正则要和字符串匹配,如果不匹配的话,结果是null
。
- 正则
RegExp.prototype
上的方法- exec
- 捕获到的结果是
null
或者数组。 - 数组第一项是本次捕获到的内容。其余项是对应小分组本次单次捕获的内容。
index
是当前捕获内容在字符串中的起始索引。input
是原始字符串。- 每执行一次,只能捕获到一个符合正则规则的,但是默认情况下,获取到的结果永远是第一个匹配到的。这是正则捕获的懒惰性。原因是因为默认情况下
lastIndex
的值不会被修改,每一次都是从字符串开始位置查找。 - 设置全局匹配符后后,
lastIndex
会自己修改。 - 当全部捕获后,再次获取后的结果是
null
,但是lastIndex
又回归为0
。
//全部获取的实现 function execAll(str=""){ let arr=[],res=this.exec(str); if(!this.global) return res; while(res){ arr.push(res[0]); res=this.exec(str); } return arr; } RegExp.prototype.execAll=execAll
1
2
3
4
5
6
7
8
9
10
11 - 捕获到的结果是
- test 加了全局修饰符后,基于
test
匹配后,lastIndex
的值也会被修改。
- exec
- 字符串
String.prototype
上支持正则表达式的方法。- replace
- match
const reg=/^\d+/g; "2018ddd2019ferwt2020".match(reg);//["2018","2019","2020"]
1
2- split
const reg1=/\d+/;
console.log(reg1.lastIndex);//0 当前正则下一次匹配的其实索引位置
const res=reg1.exec("2020afagsgsg1878787uu45");
1
2
3
2
3
# 1、正则的分组捕获
//身份证号码
let str="430431199404328765";
let reg=/^(\d{6})(\d{4})(\d{2})(\d{2})(\d)(?:\d|X)$/;
console.log(reg.exec(str));
console.log(str.match(reg));
//如果设置了分组(改变优先级),但是捕获的时候不需要单独捕获,可以基于"?:"来处理
1
2
3
4
5
6
2
3
4
5
6
//=>既要捕获到{数字},也想单独的把数字也获取到。
let str="{0}年{1}月{2}日";
let reg=/\{(d+)\}/;
console.log(reg.exec(str));//=>["{0}","0"...]
console.log(str.match(reg));//=>["{0}","0"...]
let reg1=/\{(d+)\}/g;
console.log(str.match(reg1));//=>["{0}","{1}","{2}"];在多次匹配的情况下,match只能捕获大正则匹配的内容获取到,小分组匹配的信息无法获取。
function match(str){
const arrBig=[],arrSmall=[];
const reg=/\{(d+)\}/g;
let res=reg.exec(str);
while(res){
const [big,small]=res;
arrBig.push(big);
arrSmall.push(small);
res=reg.exec(str);
}
return {
arrBig,arrSmall
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//分组的第三个作用:"分组引用"
const str="book";//=>good look moon foot
let reg=/^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/;//=>分组引用就是通过"\数字"让其和对应分组出现一模一样的内容
1
2
3
2
3
# 2、正则捕获的贪婪性
默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来获取的。
let str="james2019@2020tj";
let reg=/\d+/g;
console.log(str.match(reg));["2019","2020"];
//=>在量词元字符后面设置?:取消捕获时候的贪婪性(按照正则匹配的最短结果来获取)。
let reg1=/\d+?/g;
console.log(str.match(reg));["2","0","1","9","2","0","2","0"];
1
2
3
4
5
6
2
3
4
5
6
问号在正则中的五大作用:
- 问号左边是非量词元字符: 本身代表量词元字符,出现0到1次。
- 问号左边是量词元字符:取消捕获时候的贪婪性。
- (?:)只匹配不捕获
- (?=) 正向预查
- (?!) 负向预查
# 3、其他正则捕获的方法
# 1、test也能捕获(本意是匹配)
let str="{0}年{1}月{2}日";
let reg=/\{(d+)\}/;
console.log(reg.test(str));//true
console.log(RegExp.$1);//=>"0"
console.log(reg.test(str));//true
console.log(RegExp.$1);//=>"1"
console.log(reg.test(str));//true
console.log(RegExp.$1);//=>"2"
console.log(reg.test(str));//false
console.log(RegExp.$1);//=>"2" 存储的是上一次捕获的结果
//=>RexExp.$1~RegExp.$9:获取当前本次正则匹配后,第一个到第九个分组的信息
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 2、replace字符串中实现替换的方法(一般都是伴随正则一起使用的)
let str="jamestang2020@james2021";
str=str.replace(/([a-z]+)/g,"jie");//将jamestang james都替换成jie
1
2
2
//将时间字符串进行处理
let time="2020-12-21";//=>2020年12月21日
let reg=/^(\d{4})-(\d{1,2})-(\d{1,2})/;
time=time.replace(reg,"$1年$2月$3日");
console.log(time);//2020年12月21日
1
2
3
4
5
2
3
4
5
[str].replace([reg],[function])
- 首先拿
reg
和time
进行匹配捕获,能匹配到几次就会把传递的函数执行几次(而且是匹配一次就执行一次)。 - 不仅把方法执行,而且
replace
还给方法传递了实参信息(和exec
捕获的内容一致的信息:大正则匹配的内容,小分组匹配的信息) - 在函数中我们返回的是什么,就把当前大正则匹配到的内容替换成什么
time=time.replace(reg,(big,$1,$2,$3)=>{
//$1,$2,$3是定义的形参
console.log(big,$1,$2,$3);//=>2020-12-21 2020 12 21
})
time=time.replace(reg,(big,...arg)=>{
const [$1,$2,$3]=arg;
$2.length<2?$2="0"+$2:null;
$3.length<2?$3="0"+$3:null;
return $1+"年"+$2+"月"+$3+"日"
})
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 5、实例
# 1、单词首字母大写
let str="good good study, day day up!";
let reg=/\b([a-zA-Z])[a-zA-Z]*\b/g;
str=str.replace(reg,(...arg)=>{
let [content,$1]=arg;
$1=$1.toUpperCase();
content=content.subString(1);
return $1+content;
})
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 2、验证一个字符串中哪个字母出现的次数最多,多少次?
//方法1 排序+正则
let str="dsafsdgdgewrwrwety";
str=str.split("").sort((a,b)=>a.localeCompare(b)).join("");
let reg=/([a-zA-Z]\1+)/g;//出现次数大于1的字符
let arr=str.match(reg);
arr.sort((a,b)=>b.length-a.length);
let max=arr[0].length,res=[res[0].subStr(0,1)];
for(let i=1;i<arr.length;i++){
const item=arr[i];
if(item.length<max>){
break;
}
res.push(item.subStr(0,1))
}
//方法 2
let str="dsafsdgdgewrwrwety";
str=str.split("").sort((a,b)=>a.localeCompare(b)).join("");
let max=0,res=[],flag=false;
for(let i=str.length;i>0;i--){
let reg=new RegExp("([a-zA-Z]\\1){"+(i-1)+"}",'g');
str.replace(reg,(content,$1)=>{
res.push($1);
max=i;
flag=true;
})
if(flag) return break;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 3、时间字符串格式化、获取URL地址问号后面的参数信息、实现大数字的千分符处理
~function (){
/**
* formatTime:时间字符串格式化处理
*/
function formatTime(template="{0}年{1}月{2}日{3}时{4}分{5}秒"){
/**
* 1、获取时间字符串中的年月日信息
*/
const timeArr= this.match(/\d+/g);
return template.replace(/\{(\d+)\}/g,(...[,$1])=>{
let time=timeArr[$1]||"00";
time.length<2?time="0"+time:null;
//返回大正则匹配的内容
return time;
});
}
/**
* 获取URL地址问号后面的参数信息
* @return 把所有问号参数信息以键值对的方式存储起来并且返回
*/
function queryURLParams(){
let obj={};
this.replace(/([^?=#&])=([^?=#&])/g,(...[,$1,$2])=>obj[$1]=$2)
thia.replace(/#([^?=#&])/g,(...[,$1])=>obj["HASH"]=$1)
return obj;
}
/**
* 实现大数字的千分符处理
*/
function millimeter(){
return this.replace(/\d{1,3}(?=(\d{3}))+$/g,content=> content+",")
}
['formatTime',"queryURLParams","millimeter"].forEach(item=>{
String.prototype[item]=eval(item);
})
}()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
← 性能优化总结