目录
- RegExp
- 组成(两部分组成)
- 常用易错元字符剖析
- 常用的正则表达式
- 正则的捕获
- 正则表达式中的分组捕获和分组引用
- 正则的贪婪性
- 正则的其他捕获方法
- 正则中括号的作用
前言
RegExp不论是前端还是后端的开发同学都是会使用到。一个好的RegExp能帮你在日常的开发中,提升开发效率。在某些厂的面试中,可能也会遇到关于RegExp的相关问题。今天小编就不完全指北。
1. RegExp
RegExp 全称Regular Expression,作用,用来处理字符串规则。它是一个规则,可以验证字符串是否符合某个规则(test),也可以把字符串中符合规则的内容捕获到(exec/match)。
2. 组成(两部分组成)
1. 元字符
- 量词元字符:设置出现的次数
- “?” 代表出现零到一次
- “*” 代表零到多次
- “+” 代表一到多次
- {n} 代表出现n次
- {n,} 代表n到多次
- {n,m} 代表出现n到m次
- 特殊元字符:单个或者组合一起的特殊的含义
- \ 转义字符
- . 除\n(换行符)以外的任意字符
- ^ 以哪一个元字符作为开始
- $ 以哪一个元字符作为结束
- \d 0~9之间的一个数字
- \D 非0~9之间的一个数字
- \n换行符
- \w 数字字母下划线的一个任意字符
- \W 非数字字母下划线的一个任意字符
- \s 一个空白字符(包含空格、制表符、换页符)
- \S 非
- \b 一个单词的边界
- \t 制表符
- | x|y中一个字符
- [xyz] 或者,x或者y或者z
- [^xy] 非x,y的任意字符
- [a-z] 指定a-z这个返回中的任意字符
- [^a-z]
- () 分组
- (?:) 只匹配不捕获
- (?=) 正向预查
- (?!) 负向预查
-
2. 修饰符
i:忽略大小写匹配
- m:忽略换行匹配
- g:全局匹配
- u:含义为“Unicode 模式”,用来正确处理大于\uFFFF的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。(RegExp.prototype.unicode)
- y:“粘连”(sticky)修饰符(RegExp.prototype.sticky)。
- s:在正则中.可以匹配除\n以外的任意字符,修饰符s可以让.匹配所有字符(RegExp.prototype.dotAll)
3. 常用易错元字符剖析
1. ^ $
let reg = /^\d/;
console.log(reg.test("stone")); //=>false
console.log(reg.test("2019stone"));//=>true
console.log(reg.test("stone2019"));//=>false
let reg = /\d$/;
console.log(reg.test("stone")); //=>false
console.log(reg.test("2019stone"));//=>false
console.log(reg.test("stone2019"));//=>true
2. \
//=>.不是小数点,是除\n外的任意字符
let reg = /^2.3$/;
console.log(reg.test("2.3"));//=>true
console.log(reg.test("2@3"));//=>true
console.log(reg.test("23"));//=>false
//=>基于转义字符,让其只能代表小数点
reg = /^2\.3$/;
console.log(reg.test("2.3"));//=>true
console.log(reg.test("2@3"));//=>false
let str = "\\d";
reg = /^\d$/; //=>\d代表0-9的数字
console.log(reg.test(str)); //=>false
reg = /^\\d$/; //=>把特殊符合转换为普通的
console.log(reg.test(str)); //=>true
3. x|y
let reg = /^18|29$/;
console.log(reg.test("18")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("129")); //=>true
console.log(reg.test("189")); //=>true
console.log(reg.test("1829")); //=>true
console.log(reg.test("829")); //=>true
console.log(reg.test("182")); //=>true
//---直接x|y会存在很乱的优先级问题,一般我们写的时候都伴随着小括号进行分组,因为小括号改变处理的优先级 =>小括号:分组
reg = /^(18|29)$/;
console.log(reg.test("18")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("129")); //=>false
console.log(reg.test("189")); //=>false
//=>只能是18或者29中的一个了
4. []
//1.中括号中出现的字符一般都代表本身的含义
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
//2.中括号中不存在多位数
reg = /^[18]$/;
console.log(reg.test("1")); //=>true
console.log(reg.test("8")); //=>true
console.log(reg.test("18")); //=>false
reg = /^[10-29]$/; //=>1或者0-2或者9
console.log(reg.test("1"));//=>true
console.log(reg.test("9"));//=>true
console.log(reg.test("0"));//=>true
console.log(reg.test("2"));//=>true
console.log(reg.test("10"));//=>false
4. 常用的正则表达式
1. 验证是否为有效数字
/*
* 规则分析
* 1.可能出现 + - 号,也可能不出现 [+-]?
* 2.一位0-9都可以,多位首位不能是0 (\d|([1-9]\d+))
* 3.小数部分可能有可能没有,一旦有后面必须有小数点+数字 (\.\d+)?
*/
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/;
// 保留两位小数
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d{1,2})?$/;
2. 验证密码
//=>数字、字母、下划线
//=>6~16位
let val = userPassInp.value,
reg = /^\w{6,16}$/;
let flag=reg.test(val);
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}$/;
4. 验证邮箱
let reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
//=> \w+((-\w+)|(\.\w+))*
//1.开头是数字字母下划线(1到多位)
//2.还可以是 -数字字母下划线 或者 .数字字母下划线,整体零到多次
//=>邮箱的名字由“数字、字母、下划线、-、.”几部分组成,但是-/.不能连续出现也不能作为开始
//=> @[A-Za-z0-9]+
//1.@后面紧跟着:数字、字母 (1-多位)
//=> ((\.|-)[A-Za-z0-9]+)*
//1.对@后面名字的补充
// 多域名 .com.cn
// 企业邮箱 zxt@stone-qiaduan-office.com
//=> \.[A-Za-z0-9]+
//1. 这个匹配的是最后的域名(.com/.cn/.org/.edu/.net...)
5. 身份证号码
/*
* 1. 一共18位
* 2. 最后一位可能是X
*
* 身份证前六位:省市县 130828
* 中间八位:年月日
* 最后四位:
* 最后一位 => X或者数字
* 倒数第二位 => 偶数 女 奇数 男
* 其余的是经过算法算出来的
*/
//let reg = /^\d{17}(\d|X)$/;
//=>小括号分组的第二个作用:分组捕获,不仅可以把大正则匹配的信息捕获到,还可以单独捕获到每个小分组的内容
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
reg.exec("130828199012040617");
//=>["130828199012040617", "130828", "1990", "12", "04", "1", "7"...] 捕获结果是数组,包含每一个小分组单独获取的内容
5. 正则的捕获
1. 实现正则捕获的方法
正则RegExp.prototype上的方法
- exec
- test
字符串String.prototype上支持正则表达式的方法
- replace
- match
- splite
实现正则的前提是:当前正则要和字符串匹配,不匹配出现的结果是null。let str = "stone20190yangfan2020qihang2021";
let reg = /^\d+$/;
console.log(reg.test(str)); // => false
console.log(reg.exec(str)); // => null
2. 基于exec实现正则的捕获
- 捕获到的结果是 null 或者一个数组
- 第一项:是本次捕获到的内容
- 其余项:对应小分组本次单独捕获的内容
- index:当前捕获内容在字符串中起始的位置
- input:原始字符串
每执行一次 exec,只能捕获到一个符合正则规则的,但是默认情况下,我们不管执行多少遍,获取的结果永远都是第一个匹配到的,其余的捕获不到。
原因:默认情况下,lastIndex的值是不会被改变的,每一次都是从字符串的开始位置查找所以找到的永远是第一个。
解决方法:我们不能手动改lastIndex,我们可以使用修饰符g,设置全局匹配修饰符g之后,lastIndex会被修改。
let reg = /\d+/g;
if (reg.test(str)) {
//=>验证一下:只有正则和字符串匹配我们在捕获
console.log(reg.lastIndex); //=>11 基于TEST匹配验证后,LASTINDEX已经被修改为第一次匹配后的结果,所以下一次捕获不再从头开始了
console.log(reg.exec(str)); //=>["2020"...]
}
4. 实现一个方法execAll,执行一次可以把所有的结果都匹配到(前提是要写全局修饰符g)
~functoin() {
function execAll(str = '') {
// 先判读正则是否设置了修饰符g
if (!this.global) return this.exec(str);
// str是当前被匹配的字符串
// array存放所有的捕获结果
// res存放每一次捕获的结果,null或者一个数组
let array = [],
res = this.exec(str);
while(res) {
// 每一次捕获的内容都放在数组中
array.push(res);
// 只要捕获内容补位null,则继续捕获下去
res = this.exec(str)
}
return array;
}
RegExp.prototype.execAll = execAll;
}();
let reg = /\d+/g;
console.log(reg.execAll(str));
// execAll方法可以使用字符串的match实现,execAll就是match的实现
console.log(str.match(reg));
execAll方法可以使用字符串的match实现,execAll就是match的实现
- 但是多次匹配的情况下,match只能把大正则匹配的内容获取到,小分组匹配的信息无法获取
'-42'.match(/^([-+]?\d*).*/)
(2) ["-42", "-42", index: 0, input: "-42", groups: undefined]
'-42dasda'.match(/^([-+]?\d*).*/)
(2) ["-42dasda", "-42", index: 0, input: "-42dasda", groups: undefined]
'-42dasda'.match(/^([-+]?\d*).*/g)
["-42dasda"]
6. 正则表达式中的分组捕获和分组引用
1. 分组捕获
//=>身份证号码
let str = "130828199012040112";
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;
console.log(reg.exec(str));
console.log(str.match(reg));
//=>["130828199012040112", "130828", "1990", "12", "04", "1", index: 0, input: "130828199012040112"]
//=>第一项:大正则匹配的结果
//=>其余项:每一个小分组单独匹配捕获的结果
//=>如果设置了分组(改变优先级),但是捕获的时候不需要单独捕获,可以基于?:来处理
- 第一项:大正则匹配的结果
- 其余项:每一个小分组单独匹配捕获的结果
- 如果设置了分组(改变优先级),但是捕获的时候不需要单独捕获,我们可以使用?:来处理,?:只分组不捕获
注意:match多次匹配的情况下,match只能把大正则匹配的内容获取到,小分组匹配的信息无法获取
解决办法:
~function() {
function execAll(str = '') {
if (!this.global) return this.exec(str);
let arrayBig = [],
arraySmall = [],
res = this.exec(str);
while(res) {
let [big, small] = res;
arrayBig.push(big);
arraySmall.push(small);
res = this.exec(str)
}
}
RegExp.prototype.execAll = execAll;
}();
2. 分组引用
分组引用:就是通过“\数字”让其代表和对应分组出现一模一样的内容
//=>分组的第三个作用:“分组引用”
let str = "book"; //=>"good"、"look"、"moon"、"foot"...
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/;
console.log(reg.test("book")); //=>true
console.log(reg.test("deep")); //=>true
console.log(reg.test("some")); //=>false
7. 正则的贪婪性
1. 正则捕获的贪婪性
正则捕获的贪婪性,默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来获取的。例如:
let reg = /^\d+$/;
let str = '2019@@2020';
console.log(str.match(reg));// ['2019', '2020'];
2. 如何取消贪婪性
在量词元字符后面设置 ? 取消捕获的贪婪性(按照正则匹配的最短结果来匹配)
3. ? 在这正则的作用(五大作用)
- 问号左边不是量词元字符,表示出现0到1次
- 问号左边是量词元字符,取消正则的贪婪性
- (?:)只匹配不捕获
- (?=)正向预查
- (?:)负向预查
8. 正则的其他捕获方法
1. test也能捕获(本意是匹配)
let str = "{0}年{1}月{2}日";
let reg = /\{(\d+)\}/g;
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" 存储的是上次捕获的结果
//=>RegExp.$1~RegExp.$9:获取当前本次正则匹配后,第一个到第九个分组的信息
2. replace 字符串中实现替换的方法(一般都是伴随正则一起使用的)
let str = "stone@2019|stone@2020";
//=>把"stone"替换成"前端"
//1.不用正则,执行一次只能替换一个
/*
str = str.replace("stone","前端").replace("stone","前端");
console.log(str);
*/
//2.使用正则会简单一点
str = str.replace(/stone/g,"前端");
console.log(str);
let str = "stone@2019|stone@2020";
//=>把"stone"替换为"stone前端"
//str=str.replace("stone","stone前端").replace("stone","stone前端");
//"zhufengpeixunpeixun@2019|zhufeng@2020" 每一次替换都是从字符串第一个位置开始找的(类似于正则捕获的懒惰性)
//=>基于正则g可以实现
str = str.replace(/stone/g,"stone前端");
例子:把时间字符串进行处理
将”2020-07-01”转换成”2020年07月01日”
可以这样实现
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
let time = '2020-07-01';
time = time.replace(reg, '$1年$2月$3日');
console.log(time);// 2020年07月01日
还可以这样处理 [str].replace([reg], [function])
- 1、首先拿reg和字符串进行匹配捕获,能匹配几次就会把传递的函数执行几次(匹配一次执行一次)
- 2、不仅把方法执行,而且replace还给方法传递了实参信息,和exec捕获的内容一致的信息:大正则匹配的内容,小分组匹配的信息
- 3、执行方法中return返回的是什么,就会把大正则匹配的内容替换成什么
``javascript let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/; let time = '2020-07-01'; time = time.replace(reg, (big, $1, $2, $3) => { // 这里的$1~$3是我们自己设置的变量 console.log(big, $1, $2, $3); //2020-07-01 2020 07 01 return
${$1}年${$2}月${$3}日`; }); console.log(time); // 2020年07月01日
<a name="yFlW4"></a>
#### 例子:首字母大写
```javascript
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;
return `${$1.toUpperCase()}${content.substring(1)}`;
});
console.log(str); // Good Good Study, Day Day Up!
9. 正则中括号的作用
- 一般我们写的时候都伴随着小括号进行分组,因为小括号改变处理的优先级 =>小括号
- 小括号分组的第二个作用:分组捕获,不仅可以把大正则匹配的信息捕获到,还可以单独捕获到每个小分组的内容
- 分组引用
结束语
如果文章中什么不对或者写的不好的地方,请大家多多指正,谢谢!码字不易,点个赞加个关注吧!
题外话
笔者在「深圳虾皮」,一家口碑还不错的东南亚电商公司,2021大量招人,机会多多!快来加入我们吧!
现在有想法,还是以后有想法的同学,都可以加我微信[stone—-999]!内推你加入我们的大家庭!