什么是正则表达式
正则是一个用来处理字符串的规则
- 正则只能用来处理字符串
- 处理一些包含两个方面:
① 验证当前字符串是否符合某个规则 正则匹配
② 把一个字符串中符合规则的字符获取到 正则捕获
正则表达式的创建
let reg1 = /^\d+$/g; //=>字面量创建
let reg2 = new RegExp("^\\d+$","g"); //=>构造函数方式
元字符
正则两个斜杠之间包起来的都是“元字符” ,斜杠后面出现的都是“修饰符”
运算符 | 描述 |
---|---|
\ | 转移符(在一个普通字符转义为特殊的字符,例如:\d ,把有特殊含义转换为普通意思,例如:\. 此处的点就不是任意字符,而是一个小数点) |
^ | 以什么开头 |
$ | 以什么结尾 |
. | 除了 \n 以外的任意字符 |
\n | 匹配一个换行符 |
( ) | 正则分组,把一个大正分为几个小正则 |
( ?: ) | 正则分组,只匹配不捕获 |
( ?= ) | 正向预查 |
( ?! ) | 负向预查 |
x | y | x或者y中的其中一个 |
[xyz] | x或者y或者z中的其中一个 |
[^xyz] | 除了这三个以外的任意一个字符 |
[a-z] | a-z之间的任意一个字符 |
[^a-z] | 除了a-z以外的任意字符 |
\d | 0-9之间的一个数字 |
\D | 除了0-9之间的任意字符 |
\s | 匹配一个空白字符、空格、制表符、换页符… |
\S | 匹配任何非空白字符 |
\b | 匹配一个边界符,'ni' (z左边和i右边是边界)'ni-hao' (z左边i右边,h左边o右边是边界) |
\B | 匹配非边界符 |
\w | 匹配 数字、字母、下划线 中的任意一个 等价于 [0-9a-zA-Z]
|
普通元字符
只要在正则中出现的元字符(在基于字面量方式创建),除了特殊和有量词意义的以外,其余的都是普通元字符
量词
运算符 | 描述 |
---|---|
* | 出现零到多次 |
? | 出现零到一次 |
+ | 出现一到多次 |
{ n } | 出现n次 |
{ n, } | 出现n到多次 |
{ n, m } | 出现n到m次 |
修饰符
运算符 | 描述 |
---|---|
i | 忽略大小写 |
m | 表示多行模式 |
g | 匹配全局变量 |
正则表达式中的几个规则:
中括号的一些细节
- 中括号出现的元字符一般都是代表本身的含义
- 中括号中出现的两位数,不是两位数,而是两个数字中的任意一个
代码实例
let reg = /^.$/ //=>一个正则设置了^和$,那么代表的含义其实就是只有xxx
console.log(reg.test('n')); //true
console.log(reg.test('1')); //true
let reg = /^[.]$/ //=>中括号里面的元字符代表本身
console.log(reg.test('n')); //false
console.log(reg.test('1')); //false
console.log(reg.test('.')); //true
let reg = /^[18]$/ //=>中括号里面的两位数代表两个数
console.log(reg.test('18')); //false
console.log(reg.test('1')); //true
console.log(reg.test('8')); //true
let reg = /^[12-65]$/ // 这个正则的意思1或者2-6或者5
console.log(reg.test('13')); //false 不是12~65
console.log(reg.test('6')); //true
//年龄18-65岁之间的数
18~19 1[89]
20-59 [2-5]\d
60-65 6[0-5]
let reg = /^((1[8-9])|([2-5]\d)|(6[0-5]))$/;
//匹配[object aaa]
let reg = /^\[object .+\]$/;
小括号分组作用
- 改变默认优先级
- 分组捕获
- 分组引用
第一、改变默认优先级
let reg = /^18|19$/
console.log(reg.test('18')); //true
console.log(reg.test('19')); //true
console.log(reg.test('1819'));//true
console.log(reg.test('189')); //true
console.log(reg.test('181')); //true
console.log(reg.test('819')); //true
console.log(reg.test('119')); //true
let reg = /^(18|19)$/
console.log(reg.test('18')); //true
console.log(reg.test('19')); //true
console.log(reg.test('1819'));//false
console.log(reg.test('189')); //false
console.log(reg.test('181')); //false
console.log(reg.test('819')); //false
console.log(reg.test('119')); //false
第二、分组引用
let reg = /^([a-z])([a-z])\2\1$/; //=>正则中出现的\1代表和第一分组出现一模一样的内容...
console.log(reg.test('oppo')); //true
console.log(reg.test('poop')); //true
第三、分组捕获
// 编写一个正则匹配身份证号码
let reg = /^\d{17}(\d|X)$/; //简单:只匹配知否符合格式,不能提取身份证中的一些信息
let reg = /^\(d{6})(d{4})(d{2})(d{2})(d{2})(\d)(?:\d|X)$/
console.log(reg.exec('130987200198983789')); //实现正则捕获
问号在正则中的作用
- 量词元字符:出现零次或者一次
/-?/
让-
出现一次或者不出现- 取消贪婪性:
/\d+?/
捕获时候只捕获最短匹配的内容- 只匹配不捕获:
?:
- 正向预查:
?=
- 负向预查:
?!
正则捕获
把一个字符串中和正则匹配的部分捕获到
正则方法
exec(正则捕获)
- 实现的是正则捕获,获取的结果是一个数组,如果不匹配获取的结果是
null
- 如果想获取大正则中部分信息,我们可以把这部分使用小括号包起来,形成一个分组,这样在捕获的时候,不仅可以把大正则匹配内容捕获到,而且每一个小分组中的内容也捕获到了(分组捕获)。
- 有时写小分组不是为了捕获信息,只是为了改变优先级或者进行分组引用,此时可以在分组的前面加上
?:
,代表只匹配,不捕获。test(字符串验证):验证字符串是否符合某个规格的正则。
基于exec
可以实现正则的捕获
- 如果当前正则和字符串不匹配,捕获的结果是 null
- 如果匹配,捕获的结果是一个数组
0:大正则捕获的内容
index:正则捕获的起始索引
input:原始操作的字符串 - 执行一次exec只能捕获到第一个和正则匹配的内容(正则的懒惰性)
- 如果匹配,捕获的结果是一个数组
代码实例
let str = "nihao2018nihao2019";
let reg = /\d+/;
console.log(reg.exec(str));
基于test
匹配机制
基于
test
匹配的时候,如果设置了g
test
匹配也相当于捕获,修改了lastIndex
值
代码实例
let str = "nihao2018nihao2019";
let reg = /\d+/g;
console.log(reg.test(str)); //=>true
console.log(reg.lastIndex); //=>6
console.log(reg.exec(str)); //=>2019
正则的懒惰性和贪婪性
正则懒惰性
每一次捕获的时候,只会捕获符合正则的第一项,后面符合正则规则的也不会再继续捕获 。
解决正则捕获懒惰性,我们需要加全局修饰符g
代码实例
let str = "nihao2018nihao2019";
let reg = /\d+/g;
console.log(reg.lastIndex) //0
console.log(reg.exec(str));//['2018']
console.log(reg.lastIndex) //9
console.log(reg.exec(str));['2019']
console.log(reg.lastIndex) //18
console.log(reg.exec(str));//null
console.log(reg.lastIndex) //0
console.log(reg.exec(str));//['2018']
正则贪婪性
每一次捕获的时候,总是捕获到和正则匹配中最长的内容,例如:
2
符合\d+
2666
也符合\d+
,但捕获的是最长的内容2666
解决方案:在量词元字符后面加?
代码实例
let str = "nihao2018nihao2019";
let reg = /\d+?/g;
console.log(reg.exec(str)); //=>['2']
字符串方法
replace(字符串替换)
字符串replace实现正则捕获的方法(本身是字符串替换)split(字符串分割)
把符合正则规则的内容分割(去除)成数组match(字符串捕获)
字符串match
方法,捕获符合正则规则的内容正则捕获局限性: 在正则捕获的时候存在分组,捕获的时候不仅仅捕获把大正则匹配到的字符捕获到(数组第一项),而且把小分组匹配的内容也单独抽取出来(数组中的第二项开始就是小分组捕获的内容——分组捕获),而
?:
是用来阻止分组捕获内容的,“只匹配不捕获”。
第一、match捕获
代码实例
let str = "nihao2018nihao2019";
let reg = /\d+/g;
str.match(reg); //=>["2018", "2019"]
match方法局限性:match捕获的内容只有大正则捕获的,小分组内容没有单独抽取出来。
let str = "nihao{2018}nihao{2019}";
let reg = /\{(\d+)\}/g;
console.log(reg.exec(str)); //=>["{2018}", "2018", index: 5, input: "nihao{2018}nihao{2019}", groups: undefined]
console.log(str.match(reg)); //=>["{2018}", "{2019}"] match方法也有自己的局限性,在正则设置了g的情况下,基于match捕获的内容只有大正则捕获的,小分组内容没有单独抽取出来。
第二、split捕获
把匹配符合正则规则的字符串分割成数组
代码示例
let str = '2019/5/20 18:30:00',
ary = str.split(/(?:\/| |:)/g); //=> 匹配 "/"、" "、":" 作为分割
console.log(ary); //=>["2019", "5", "20", "18", "30", "00"]
第三、replace捕获
replace实现正则捕获的方法(本身是字符串替换)
代码实例
//将nihao替换成你好
let str = "nihao2018nihao2019";
str.replace(/nihao/g,'你好');
replace 原理
let str = "nihao{val:2018}nihao{val:2019}",
reg = /\{val:(\d+)\}/g
str.replace(reg,"@"); //=>每一次把当前“大正则”匹配的结果用第二个传递的字符串替换掉
console.log(str); //=>'nihao@nihao@'
str.replace(reg,"$1"); //=>$1不是拿这个字符串替换掉大正则匹配的内容,此处的$1代表第一个分组匹配的内容,等价于:RegExp.$1
console.log(str); //=>'nihao2018nihao2019'
/*
1. reg 和 str 匹配多少次,函数就触发了多少次,而且传递了一些参数信息值
2. 每一次arg中存储的信息,和执行exec捕获的信息相似(内置原理:每一次正则匹配到结果,都把函数执行,然后基于exec把本次匹配的信息捕获到,然后把捕获的信息传递个这个函数)
3. 每一次函数中返回的是什么,就把大正则匹配的内容替换成什么
*/
str.replace(reg,(...rag)=>{
console.log(arg);
return 'AA';
});
console.log(str); //=> 'nihaoAAnihaoAA'
综合练习
//事件字符串格式化 "2019/4/30 18:30:23" => "04-30 17:50"
//方法一:获取时间字符串中所有数字(split)
let str = '2019/4/30 18:30:23';
ary = str.split(/(?:\/| |:)/g);
//function fn (time) {return time<10?'0'+time:null;}
let [,month,day,hours,minutes] = ary,
//result = `fn(${month})-fn(${day}) fn(${hours}):fn(${minutes})`;
result = `${month}-${day} ${hours}:${minutes}`;
console.log(result)
//方法二:
/*
1. 利用match方法获取到时间数组;
2. 指定最后想要的时间格式,我们基于这个数组中的内容拼接好即可
*/
let str = '2019/4/30 18:30:23';
//map相对于forEach来讲多了返回值,函数中return是什么,就是当做数组迭代的这一项替换成什么。
let ary = str.match(/\d+/g).map(item=>{
return item<10?'0'+item:item;
});
let timeFormat = {0}年{1}月{2}日 {3}时{4}分{5}秒
timeFormat.replacr(/\{\d\}/g,(...arg)=>{
let [,index] = arg;
return ary[index];
});
//在正则原型上扩展一个方法,把符合正则规则的捕获到
RegExp.prototype.myExecAll = function(str) {
let result = [],
ExecAry = this.exec(str);
//为了防止出现死循环:我们检测一下正则是否加g,没有加g只把第一次捕获的返回即可。
if (!this.global) {
return this.exec(str);
}
while (ExecAry) {
result.push(ExecAry[0]);
ExecAry = this.exce(str);
}
return result;
}
console.log(reg.myExecAll(str));
//在String原型上增加一个格式化时间格式方法
String.prototype.myTimeFormat = function myTimeFormat(template = '{0}年{1}月{2}日 {3}时{4}分{5}秒') {
let ary = this.match(/\d+/g).map((item => item < 10 ? "0" + item : item));
return template.replace(/\{(\d)}/g, (...[, index]) => ary[index] || '00'); // 如果没有则为00代替
}
let str = '2019/4/30 18:30:23';
console.log(str.myTimeFormat('{1}月{2}日')); //04月30日
console.log(str.myTimeFormat('{1}-{2} {3}:{4}')); //04-30 18:30
常用的正则表达式
//匹配身份证号码
let reg = /^\(d{6})(d{4})(d{2})(d{2})(d{2})(\d)(?:\d|X)$/
console.log(reg.exec('130987200198983789')); //实现正则捕获
//匹配有效数字正则表达式
/*分析规则
1. 可以出现+/-号:可以没有,也可以有一个
2. 整数一位或者多位,多位数不能以0开头
3. 小数部分:可能有可能没有,有小数点后面至少要跟一位数字
*/
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/;
//正则手机号码验证
/*分析规则:
1. 11位数字组成
2. 以1开头
*/
let reg = /^1\d{10}&/;
//中文汉字
/*分析规则:
1. 中文汉字[\u4E00-\u9FA5]
*/
let reg = /^[\u4E00-\u9FA5]{2,}(·[\u4E00-\u9FA5]{2,})?&/;
//邮箱
/*分析规则:
1. xxxxx@xxxx.xx.xx
2. 第一部分:数字、字母、下划线、"-" 、".",但是"-"和"."不能作为开头,不能连续出现 "-" 或者 "."
*/
let reg = /^\w+([-.]\w+)*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)&/;
//匹配前后有空格字母
let reg = /^ +| +$/g