什么是正则表达式
- 是一个规则,只能处理字符串,
- 验证字符串是否符合某个规则(test)
- 内容捕获到(exec / match...) ,对字符串中符合规则的内容进行捕获
正则创建方式
- 字面量创建方式
let reg1 = /\d+/g;
- 构造函数模式创建
let reg2 = new RegExp("\\d+","g");
- 包含变量的创建方式
两个斜杠中间包起来的都是元字符(如果正则中包含某个变量的值则不能使用字面量方式创建)
这种情况只能使用构造函数方式(因为它传递的是字符串,才能用字符串拼接)
let type = "kendeji",
reg = new RegExp("^@"+type+"@$")
console.log(reg.test('@kendeji@'))
4.构造函数因为传递的是字符串,\需要写两个才代表斜杠
let reg = /\d+/g;
reg = new RegExp("\\d+","g");
常用正则元字符&修饰符
常用的元字符
1.量词元字:设置出现的次数
| 元字符 | 含义 |
|---|---|
| * | 零到多次 |
| + | 一到多次 |
| ? | 零次或者一次 |
| {n} | 出现n次 |
| {n,} | 出现n到多次 |
| {n,m} | 出现n到m次 |
2.特殊元字符:单个或者组合在一起代表特殊的含义
| 元字符 | 含义 |
|---|---|
| \ | 转义字符(普通->特殊->普通) |
| . | 除\n(换行符)以外的任意字符| |
| 以哪一个元字符作为开始 | |
| $ | 以哪一个元字符作为结束 |
| \n | 换行符 |
| \d | 0~9之间的一个数字 |
| \D | 非0~9之间的一个数字 (大写和小写的意思是相反的) |
| \w | 数字、字母、下划线中的任意一个字符 |
| \s | 一个空白字符(包含空格、制表符、换页符等) |
| \t | 一个制表符(一个TAB键:四个空格) |
| \b | 匹配一个单词的边界 |
| x竖线y | x或者y中的一个字符 |
| [xyz] | x或者y或者z中的一个字符 |
| [^xy] | 除了x/y以外的任意字符 |
| [xyz] | x或者y或者z中的一个字符 |
| [^xy] | 除了x/y以外的任意字符 |
| [a-z] | 指定a-z这个范围中的任意字符 [0-9a-zA-Z_]===\w |
| [^a-z] | 上一个的取反“非” |
| () | 正则中的分组符号 |
| (?:) | 只匹配不捕获 |
| (?=) | 正向预查 |
| (?!) | 负向预查 |
3.普通元字符:代表本身含义的
| 元字符 | 含义 |
|---|---|
| /kendeji/ | 此正则匹配的就是"kendeji" |
常用的修饰符
| 修饰字符 | 含义 |
|---|---|
| i | 忽略单词大小写匹配 |
| m | 可以进行多行匹配 |
| g | 全局匹配 |
元字符详细解析
^ $
let reg = /^\d/;
console.log(reg.test("kendeji")); //=>false
console.log(reg.test("2019kendeji"));//=>true
console.log(reg.test("kendeji2019"));//=>false
let reg = /\d$/;
console.log(reg.test("kendeji")); //=>false
console.log(reg.test("2019kendeji"));//=>false
console.log(reg.test("kendeji2019"));//=>true
- ^或$两个都不加:字符串中包含符合规则的内容即可
let reg1 = /\d+/;
- ^或$两个都加:字符串只能是和规则一致的内容
let reg2 = /^\d+$/;
举个例子:验证手机号码(11位,第一个数字是1即可)
let reg = /^1\d{10}$/;
\
- . 不是小数点,是除\n外的任意字符,基于转义字符,让其只能代表小数点
let reg = /^5.3$/;
console.log(reg.test("5.3"));//=>true
console.log(reg.test("5@3"));//=>true
console.log(reg.test("53"));//=>false
reg = /^5\.3$/;
console.log(reg.test("5.3"));//=>true
console.log(reg.test("5@3"));//=>false
- 把特殊符号转换为普通的 匹配'\d'字符串
let str = "\\d";
//字符串\\d 表示\d
reg = /^\d$/;
//=>\d代表0-9的数字
console.log(reg.test(str)); //=>false
reg = /^\\d$/;
//=>把特殊符号转换为普通的
console.log(reg.test(str)); //=>true
x|y
x|y会有优先级问题,
解决:小括号进行分组,改变优先级
let reg = /^13|29$/;
console.log(reg.test("13")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("139")); //=>true
reg = /^(13|29)$/;
//只能是13或者29中的一个
console.log(reg.test("13")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("139")); //=>false
[]
- 中括号中出现的字符一般都代表本身的含义
let reg = /^[@+]$/;
/^[]/
console.log(reg.test("@")); //=>true
console.log(reg.test("+")); //=>true
console.log(reg.test("@@")); //=>false
console.log(reg.test("@+")); //=>false
reg = /^[\d]$/; //=>\d在中括号中还是0-9
console.log(reg.test("d"));//=>false
console.log(reg.test("\\"));//=>false
console.log(reg.test("9"));//=>true
- 中括号中不存在多位数
reg = /^[13]$/;
console.log(reg.test("1")); //=>true
console.log(reg.test("3")); //=>true
console.log(reg.test("13")); //=>false
reg = /^[13-55]$/; //=>1或者3-5或者5
console.log(reg.test("1"));//=>true
console.log(reg.test("3"));//=>true
console.log(reg.test("13"));//=>false
console.log(reg.test("55"));//=>false
常用的正则表达式
验证是否为有效数字
分析:
- 可能出现 + - 号,也可能不出现
- 一位0-9都可以,多位首位不能是0
- 小数部分可能有可能没有,一旦有后面必须有小数点+数字
//1.[+-]?
//2.(\d|([1-9]\d+))
//3.(\.\d+)?
let reg = /^[+-]?(\d|[1-9]\d+)(\.\d+)?$/;
验证密码
- 数字、字母、下划线 2.6~16位
let reg = /^\w{6,16}$/
验证邮箱
let reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
分析:
比如:
huang-mei-mei
huang-mei.mei
hang.mei.mei
let reg = /^\w+((-\w+)|(\.\w+))*$/
@后面:数字、字母 (1-多位)
@[A-Za-z0-9]+
对@后面名字的补充
多域名 .com.cn
企业邮箱 hkl@huang-meimei-home.com
((\.|-)[A-Za-z0-9]+)*
这个匹配的是最后的域名(.com/.cn/.org/.edu/.net...)
\.[A-Za-z0-9]+
正则的捕获
实现正则捕获的办法
| 正则RegExp.prototype上的方法 | 字符串String.prototype上支持正则表达式处理的方法 |
|---|---|
| exec | replace |
| test | match |
| splite |
exec正则捕获
let str = "kendeji2019maidanglao2020bishengke2021";
let reg = /\d+/;
console.log(reg.exec(str))
"2019" //本次捕获到的内容
groups: undefined //对应小分组本次单独捕获的内容
index: 7 //当前捕获内容在字符串中的起始索引
input: "kendeji2019maidanglao2020bishengke2021" //原始字符串
length: 1
问题: 结果永远都是第一个匹配到的,其余的捕获不到
解决办法: 全局修饰符g
match正则捕获
let str = "kendeji2019maidanglao2020bishengke2021";
let reg = /\d+/g;
console.log(str.match(reg))
// ['2019', '2020', '2021']
正则捕获的懒惰性
正则捕获的懒惰性,默认只捕获第一个
问题: 每一次执行exec,结果永远都是第一个匹配到的,其余的捕获不到
原因: 默认情况下lastIndex的值不会被修改,每一次都是从字符串开始位置查找
解决办法: 全局修饰符g
设置全局匹配修饰符g后,第一次匹配完,lastIndex会自己修改。当全部捕获后,再次捕获的结果是 null,lastIndex变为初始值零,再次捕获又从第一个开始了
let str = "kendeji2019maidanglao2020bishengke2021";
let reg = /\d+/g;
console.log(reg.exec(str))
//['2019', index: 7, input: 'kendeji2019maidanglao2020bishengke2021', groups: undefined]
console.log(reg.exec(str))
//['2020', index: 21, input: 'kendeji2019maidanglao2020bishengke2021', groups: undefined]
console.log(reg.exec(str))
//['2021', index: 34, input: 'kendeji2019maidanglao2020bishengke2021', groups: undefined]
exec问题:
每执行一次,lastIndex改变一次,要多次执行
解决:
1.字符串中的MATCH方法,可以在执行一次的情况下,捕获到所有匹配的数据(前提:正则也得设置G才可以)
let str = "kendeji2019maidanglao2020bishengke2021";
let reg = /\d+/g;
console.log(str.match(reg))
// ['2019', '2020', '2021']
默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来获取的
- 编写一个方法execAll,执行一次可以把所有匹配的结果捕获到(前提正则一定要设置全局修饰符g)
~ function () {
function execAll(str = "") {
//=>str:要匹配的字符串
//=>this:RegExp的实例(当前操作的正则)
//=>进来后的第一件事,是验证当前正则是否设置了G,不设置则不能在进行循环捕获了,否则会导致死循环
if (!this.global) return this.exec(str);
//=>ARY存储最后所有捕获的信息 RES存储每一次捕获的内容(数组)
let ary = [],
res = this.exec(str);
while (res) {
//=>把每一次捕获的内容RES[0]存放到数组中
ary.push(res[0]);
//=>只要捕获的内容不为NULL,则继续捕获下去
res = this.exec(str);
}
return ary.length === 0 ? null : ary;
}
RegExp.prototype.execAll = execAll;
}();
let reg = /\d+/g;
console.log(reg.execAll("kendeji2019maidanglao2020bishengke2021"));
正则捕获的贪婪性
let str = "kendeji2015@1238bihsengke";
let reg = /\d+/g
console.log(str.match(reg))
// ['2015', '1238']
在量词元字符后面设置?,取消捕获时候的贪婪性(按照正则匹配的最短结果来获取)
let str = "kendeji2015@1238bihsengke";
let reg = /\d+?/g;
console.log(str.match(reg))
//['2', '0', '1', '5', '1', '2', '3', '8']
正则的分组捕获
let str = '135368199812320168'
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d)$/
console.log(reg.exec(str));
console.log(str.match(reg))
//['135368199812320168', '135368', '1998', '12', '32', '6', '8', index: 0, input: '135368199812320168', groups: undefined]
- 捕获的时候不需要单独捕获,添加?:
let str = '135368199812320168'
let reg = /^(\d{6})(?:\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d)$/
console.log(reg.exec(str));
console.log(str.match(reg))
// ['135368199812320168', '135368', '12', '32', '6', index: 0, input: '135368199812320168', groups: undefined]
- match分组捕获的问题
需求: 字符串‘{0}年{1}月{2}日’ ,要匹配{0} 和0
let reg = /\{(\d+)\}/
let str = "{0}年{1}月{2}日"
console.log(reg.exec(str));
console.log(str.match(reg))
//['{0}', '0', index: 0, input: '{0}年{1}月{2}日', groups: undefined]
问题: 正则不设置g只匹配一次,exec和match结果一样,设置g后,match小分组匹配的信息无法获取 解决:
let reg = /\{(\d+)\}/g;
let aryBig=[],
arySmall=[],
res=reg.exec(str);
while(res){
let [big,small]=res;
aryBig.push(big);
arySmall.push(small);
res=reg.exec(str);
}
console.log(aryBig,arySmall); //=>["{0}", "{1}", "{2}"] ["0", "1", "2"]
分组引用
分组引用就是通过“\数字”让其代表和对应分组出现一模一样的内容
let str = "keep";
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/;
console.log(reg.test("beek")); //=>true
console.log(reg.test("deep")); //=>true
console.log(reg.test("hope")); //=>false
总结:问号在正则中的五大作用
- 问号左边是非量词元字符:本身代表量词元字符,出现零到一次
let reg = /\d+/
2.问号左边是量词元字符:取消捕获时候的贪婪性
let str = "kendeji2015@1238bihsengke";
let reg = /\d+?/g;
console.log(str.match(reg)) //['2', '0', '1', '5', '1', '2', '3', '8']
- (?:) 只匹配不捕获
let str = '135368199812320168'
let reg = /^(\d{6})(?:\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d)$/
console.log(reg.exec(str));
console.log(str.match(reg))
// ['135368199812320168', '135368', '12', '32', '6', index: 0, input: '135368199812320168', groups: undefined]
- (?=) 正向预查
- (?!) 负向预查
总结:小括号在正则中的三大作用
- 改变优先级
匹配13或29
错误写法:
let reg = /^13|29$/;
console.log(reg.test("13")); //=>true console.log(reg.test("29")); //=>true console.log(reg.test("139")); //=>true
console.log(reg.test("132")); //=>true
用小括号改变优先级
reg = /^(13|29)$/; //只能是13或者29中的一个
console.log(reg.test("13")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("139")); //=>false
2.分组捕获
let str = '135368199812320168'
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d)$/
console.log(reg.exec(str));
console.log(str.match(reg))
//['135368199812320168', '135368', '1998', '12', '32', '6', '8', index: 0, input: '135368199812320168', groups: undefined]
- 分组引用
let str = "keep";
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/;
console.log(reg.test("beek")); //=>true
console.log(reg.test("deep")); //=>true
console.log(reg.test("hope")); //=>false
replace字符串中实现替换
- 'kendejig@222|kendeji@2222',
把kendejig替换为KFC。
let str = 'kendejig@222|kendeji@2222'
str = str.replace(/kendeji/g,"KFC");
console.log(str);
//KFCg@222|KFC@2222
练习
时间字符串处理
"2020-03-18" 变为 2020年03月18日
方法一:
let str = '2020-03-18'
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
str = str.replace(reg,"$1年$2月$3日")
console.log(str)
//2020年03月18日
方法二:
[str].replace([reg],[function])
用str与reg匹配,匹配一次function就执行一次 ,
在函数中我们返回的是什么,就把当前大正则匹配的内容替换成什么
let str = '2020-03-18'
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
str = str.replace(reg,(big,$1,$2,$3)=>{
//=>这里的$1~$3是我们自己设置的变量
//=> big 大正则匹配的内容
console.log(big,$1,$2,$3);
});
str = str.replace(reg,(...arg)=>{
let [,$1,$2,$3]=arg;
$2.length<2?$2="0"+$2:null;
$3.length<2?$3="0"+$3:null;
return $1+"年"+$2+"月"+$3+"日";
});
时间字符串的格式化处理
let str = '2016/05/12',
templete = '{0}年{1}月{2}日 {3}时{4}分{5}秒'
function formatTime() {
let timeAry = str.match(/\d+/g);
return templete.replace(/\{(\d+)\}/g, (...[, $1]) => {
let time = timeAry[$1] || "00";
return time.length < 2 ? "0" + time : time;
});
}
console.log(formatTime(templete,str))//2016年05月12日 00时00分00秒
templete = '{0}-{1}-{2} {3}:{4}:{5}'
console.log(formatTime(templete,str)) //2016-05-12 00:00:00
单词首字母大写
let str = "you can you up a"
let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;
//=>函数被执行了五次,每一次都把正则匹配信息传递给函数
//=>每一次ARG:["you","y"] ["can","c"] ["you","y"]...
str = str.replace(reg,(...arg)=>{
let [content,$1]=arg;
$1=$1.toUpperCase();
content=content.substring(1);
return $1+content;
});
console.log(str)
//You Can You Up A
queryURLParams :获取URL地址问号和面的参数信息(可能也包含HASH值)
let url = 'http://www.javascriptpekkk.cn/course/1935/task/114966/show?a=b&c=d#test'
function queryURLParams() {
let obj = {};
this.replace(/([^?=&#]+)=([^?=&#]+)/g, (...[, $1, $2]) => obj[$1] = $2);
this.replace(/#([^?=&#]+)/g, (...[, $1]) => obj['HASH'] = $1);
return obj;
}
正向预查,实现大数字的千分符处理
return this.replace(/\d{1,3}(?=(\d{3})+$)/g, content => content + ',');
}
split
let str = `content-encoding: gzip
content-type: text/html; charset=utf-8
date: Sat, 16 Jan 2021 09:17:16 GMT
server: yunjiasu
tracecode: 10366604600460907786011617
vary: Accept-Encoding
yjs-id: 9b71301e63182cf0-115`
str = str.split(/(?:\n)/g);
console.log(str)
//['content-encoding: gzip', ' content-type: text/html; charset=utf-8', ' date: Sat, 16 Jan 2021 09:17:16 GMT', ' server: yunjiasu', ' tracecode: 10366604600460907786011617', ' vary: Accept-Encoding', ' yjs-id: 9b71301e63182cf0-115']