创建方式
创建方式有两种
//字面量方式,两个斜杠包起来一些正则元字符
let reg = /\d+/g;
//构造函数方式,注意斜杠转义
let reg = new RegExp("\\d+", "g");
两种创建方式的区别
1、构造函数方式要特别注意反斜杠转义,因为在字符串中 \ 本来就特殊,比如你想输出"\"
let str1 = '\' //会报错
let str2 = '\\' //正确
2、字面量方式不能插入变量,因为//中间的都是正则元字符,所以要插入变量创建正则的话就只能用构造函数方式
let name = 'sam';
let reg1 = /^--+name+--$/;
let reg2 = new RegExp("^--"+name+"--$");
reg1.test('--sam--') //false
reg2.test('--sam--') //true
正则表达式由两个部分组成
元字符
常用量词元字符
* 零到多次
+ 1到多次
? 0次或1次
{n} 出现n次
{n,} 出现n到多次
{n,m} 出现n到m次
特殊元字符
\ 转义字符
. 除\n(换行符)以外的任意字符
^ 以哪个元字符开始
$ 以哪个元字符结束
\n 换行符
\b 单词边界
\d 0-9之间的一个数字
\D 非\d
\w 数字、字母、下划线中任意一个字符
\W 非\w
\s 一个空白字符(包括空格、制表符、换页符等)
\t 一个制表符(Tab键:4个空格)
x|y x或y中的一个字符
[xyz] x或y或z中的一个字符
[^xy] 除了x和y以外的任意字符
[a-z] 指定a-z范围内的任意字符 //[0-9a-zA-Z_]===\w
[^a-z] 非上一个
() 正则中的分组
(?:) 只匹配不捕获
(?=) 正向预查
(?!) 反向预查
修饰符
i ignoreCase 忽略大小写
m multiline 可以多行匹配
g global 全局匹配
或 |
| 直接使用的时候可能会出现一些不确定性,比如:
let reg = /^18|29$/;
reg.test(18) //true
reg.test(29) //true
reg.test(129) //true
reg.test(189) //true
reg.test(229) //true
reg.test(1236629) //true
//这时reg匹配的是/(^18)|(29$)/以18开始或者是29结束
//如果你想匹配的是18或者29,那么
reg = /^(18|29)$/;
所以,避免不确定性,建议在使用 | 时加上小括号。
小括号()
分组,提高优先级,配合 | 使用
参照上文
分组引用
通过"\数字"让该位置和对应分组(根据数字指定第几个分组)的内容相同。
//比如我想匹配像book这样第二和第三个字母一样的单词
var reg = /^[a-zA-Z]{4}$/; //这样达是不到要求
reg.test('book'); //true
reg.test('boak'); //true
reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/; //\1位置内容与第一个分组内容相同
reg.test('book'); //true
reg.test('boak'); //false
reg = /^([a-zA-Z])[a-zA-Z]\1[a-zA-Z]$/; //\1位置内容与第一个分组内容相同
reg.test('book'); //false
reg.test('boak'); //false
reg.test('bobk'); //true
中括号[]
[]里面没有多位数
[]里面字符大多为原意
正则捕获
exec()
这是RegExp的方法。
- 返回null或一个数组。
- 每次只捕获一个匹配项。
- 如果正则表达式带修饰符g的话,则每执行完一次就会修改正则实例的lastIndex属性,从而实现捕获多个匹配项。
//这是一个国内身份证号码的正则,之所以加这么多小括号是为了便于分组捕获一些关键信息
let reg = /^([1-8]\d{5})([1-2]\d{3})([0-1]\d)([0-3]\d)\d{2}(\d)(\d|X)$/;
reg.exec('431101199912311193');
//捕获结果["431101199912311193", "431101", "1999", "12", "31", "9", "3", index: 0, input: "431101199912311193", groups: undefined]
//对应解释[捕获到的内容, 分组1内容, 分组2内容, 分组3内容, 分组4内容, 分组5内容, 分组6内容, index:开始匹配内容时的索引, input:原文本 ...]
注意:如果正则表达式不带修饰符g的话,那么exec()之后reg.lastIndex还是0,如果带g的话,test()和exec()都会改变lastIndex。
match()
这是String的方法。
- 返回null或一个数组。
- 如果正则表达式不带修饰符g的话,match()和exec()返回值一样,效果一样。
//如果不带g
let reg = /\d+/;
reg.exec('2099年12月31日');//["2099", index: 0, input: "2099年12月31日", groups: undefined]
'2099年12月31日'.match(reg);//["2099", index: 0, input: "2099年12月31日", groups: undefined]
//如果带g
let reg = /\d+/g;
reg.exec('2099年12月31日');//["2099", index: 0, input: "2099年12月31日", groups: undefined]
'2099年12月31日'.match(reg);//["2099", "12", "31"]
replace()
这是String的方法。
- 返回一个字符串(不改变原字符串)。
- 这个函数更多的是配合正则使用。
- 如果第二个参数是函数,那么如果正则不匹配的话,该函数不会执行。
案例:时间字符串处理
//时间字符串处理
let time = '2099-12-31';
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
time.replace(reg, '$1年$2月$3日'); //2099年12月31日
案例:单词首字母大写
当replace第二个参数是函数时,正则匹配几次该函数就会执行几次,每次的函数实参跟exec()捕获结果一致([匹配的字符串,分组1,分组2...,原文本])。
//单词首字母大写
let str = 'good good study, day day up!';
let reg = /\b([a-zA-z])([a-zA-z]*)\b/g;
str.replace(reg, (...args) => {
let [, $1, $2] = args;
return $1.toUpperCase() + $2;
}) //"Good Good Study, Day Day Up!"
正则的贪婪性
默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来捕获的,称为正则捕获的贪婪性。
在量词元字符后面加上 ? 即可取消正则捕获贪婪性(按照正则匹配的最短结果捕获)。
let str = '2099年12月31日';
//贪婪匹配
let reg = /\d+/g;
str.match(reg); //["2099", "12", "31"]
//懒惰匹配
reg = /\d+?/g;
str.match(reg); //["2", "0", "9", "9", "1", "2", "3", "1"]
RegExp.$1-$9
RegExp.$1,RegExp.$2...,RegExp.$9分别表示当前正则实例匹配捕获到的分组捕获内容。$1表示正则表达式里的第一个分组,如此类推,这里最多9个。
let str = '2099年12月31日';
let reg = /\d+/g;
reg.test(str);
console.log(RegExp.$1) //""
reg = /(\d+)/g; //正则表达式里必须要有分组才有效果
reg.test(str);
console.log(RegExp.$1) //2099
reg.test(str); //前面说到test()也是会改变lastIndex的
console.log(RegExp.$1) //12
reg.test(str);
console.log(RegExp.$1) //31
正则不包含
^((?!不想包含的字符串).)*$
数字千分符
普通方法
let num = '3214567812';
num = num.split('').reverse().join(''); //字符串倒序
//字符串还剩1,2位的时候就不用再循环了,避免出现',321,123'这种情况
for(let i = 2; i < num.length - 1; i += 4){
let left = num.substring(0, i+1);
let right = num.substring(i+1);
num = left + ',' + right;
}
num = num.split('').reverse().join(''); //字符串倒序回来
console.log(num) //"3,214,567,812"
正则方法
- 捕获后面到结束位是3为数字或3的倍数位数字(?=(\d{3})+$)的1-3位数字(\d{1,3})
let num = '3214567812';
//?= ?!都是只匹配不捕获
num = num.replace(/\d{1,3}(?=(\d{3})+$)/g, c => c + ',');
console.log(num) //"3,214,567,812"
常用的正则表达式
1、验证是否为有效数字
分析:
- 第一位有可能是+、- [+-]?
- 如果是一位数呢可以是0,如果是多位数那第一位不能是0 (\d|([1-9]\d+))
- 是否有小数点 (\.\d+)?
//验证是否为有效数字
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
2、真实汉字姓名
分析:
- 汉字 /^[\u4E00-\u9FA5]$/
- 长度 2-10位
- 可能是译名,中间有 ·,默认最多出现两次
//验证真实汉字姓名
let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/
3、国内身份证
分析:
- 第一位数(省份只有1-8开头) [1-8]
- 第七位数(年份只有1或2开头) [1-2]
- 第十一位数(月份只有0或1开头) [0-1]
- 第十三位数(日期只有0-3开头) [0-3]
- 最后一位数是数字或大写X
//验证国内身份证号码
let reg = /^([1-8]\d{5})([1-2]\d{3})([0-1]\d)([0-3]\d)\d{2}(\d)(\d|X)$/
//之所以加这么多小括号是为了便于分组捕获一些关键信息
4、url网址解析
分析:
- 协议(可有可无) ((http|https|ftp):\/\/)?
- 域名 \w或-多个,以.连接,最后域名后缀只能是数字或字母 (([\w-]+\.)+[a-z0-9]+)
- 请求地址(可有可无)不包含?# ((\/[^/?#]*)+)?
- 问号传参(可有可无) (\?[^#]+)?
- 哈希(可有可无) (#.+)?
let reg = /^(?:(http|https|ftp):\/\/)?((?:[\w-]+\.)+[a-z0-9]+)((?:\/[^/?#]*)+)?(\?[^#]+)?(#.+)?$/i;
let url = 'https://cn.bing.com/search?q=jszaixian&qs=n&form=QBRE&sp=-1&pq=jszaixian&sc=1-9&sk=&cvid=4A0A125EC6EB47ADA79D8234F64C01F3';
console.log(reg.exec(url))
//协议:https
//域名:cn.bing.com
//请求地址:/search
//问号传参:?q=jszaixian&qs=n&form=QBRE&sp=-1&pq=jszaixian&sc=1-9&sk=&cvid=4A0A125EC6EB47ADA79D8234F64C01F3
//哈希:undefined