本文原创:liuchendi
1.正则表达式的基本概念
1.1 捕获/匹配的概念
- 匹配:验证当前字符串是否符合某个规则
- 捕获:将字符串中符合规则的字符获取到
1.2 正则表达式的创建
字面量方式创建
let reg = /元字符/修饰符
实例方式创建
letg reg = new RegExp('\d+',igm)//实例化创建正则的方式不需要加/(并不包括转义斜杠\),并把修饰符以第二个参数的形式传递给实例
注意:字面量方式创建的正则表达式无法识别变量,但实例方式创建的可以
通过实例方式创建的正则,其中特殊元字符需要再加上一个\,因为字符串中的\也是代表转义的意思,就会默认省略
let reg = new RegExp('\\b${cn}\\b')
正则表达式无法识别连续的单词中的字符或者数字,他会默认把他们当做一个一个单独的字符去识别
let reg = /[12-66]/ //表示1或2-6或5
回车打下的换行还自带一个空格
当你要检测字符串\d时
let reg = /\\d/
reg.test('\\d')
//字符串中的斜杠首先就是一个转义运算符,你要想测试\d
//字符串本身的\还要进行转义
1.3 元字符的分类
- 特殊元字符(在正则表达式中有特殊意义的字符)
- 量词元字符(限制元字符出现的连续次数)
- 其他元字符
- 普通元字符
1.3.1 特殊元字符
- \ 转义元字符(把一些运算符转换成原意)
- \d 出现0-9中的任意一位数字
- \D 出现除了0-9中的任意一位数字
- \w 匹配字母(包括大小写)、数字、下划线内的任意一个字符
- \W 匹配除字母(包括大小写)、数字、下划线以外任何一个字符
- \s 匹配空白符(空格 换行符)
- \S 匹配除空白符以外的任意一个字符
- \b 匹配单词的边界
- \B 匹配不在边界的字符
- ^ 匹配字符串的开头(以什么什么开头)
- $ 匹配以什么什么结尾
- \n 匹配换行符
- . 匹配除换行符以外的其他字符(相当于/N)
1.3.2 量词元字符
量词限制的是前面紧跟的某个字符出现的次数
- ?出现0~1次,可有可无,相当于{0,1}
- . 出现0到多次(可有可无)可以连续出现多次。相当于 {0,}
- 加号 出现1到多次(至少要出现1次)可以连续出现多次。相当于 {1,}
- {n} 连续出现n次 n代表次数
- {n,} 连续出现n到多次 至少要连续出现n次 多了不限
- {n,m} 连续出现n到m次 至少要连续出现n次 最多连续出现m次
let reg = /^[a-z]$/
console.log(reg.test('aa')) // false 注意:这个是表示一个字母既是开头又是结尾,并且是一个字母
1.3.3 其他元字符
- | x|y:匹配x或y中的任意一个字符,相当于[xy]
- [] [xyz]:匹配xyz中的任意一个字符,相当于x|y|z
- [^] [^xyz]:匹配除xyz以外的任意一个字符
- [a-z][0-9]:匹配从a-z中的任意一个字母
1.3.4 正则中的修饰符
- g global全局匹配
- i ignoreCase忽略大小写
- m multiline多行匹配
1.4 正则中的小括号
正则当中小括号改变优先级
let reg = /^a|b$/ //以a为开头或者以b为结尾
let reg3 = /^(a|b)$/ //相当于/^a$/或/^b$/
分组捕获
分组引用
let reg = /^1(\d)\1\1$/
console.log(reg.test('1222')) // true
console.log(reg.test('1333')) // true
let reg2 = /^([a-z])([a-z])\2\1$/
console.log(reg2.test('abba')) // true
console.log(reg2.test('baba')) // false
在这里如何计算他是第几个分组,就从左往右看,看他的左括号是第几个
1.5 正则中的中括号
- 中括号中部分元字符,代表的是原义 [?] [*] [+] [.]
- 中括号限制范围[a-z]、[0-9]:如果是[9-0]会报错,并且由于正则表达式无法识别连续的数字和单词,所以[20-31]实际上识别的是2|[0-3]|1
2.正则表达式的常用方法
2.1 正则的捕获
正则捕获:将匹配到的内容,捕获出来,作为返回值返回给我们。方法的位置:RegExp.prototype.exec()
他的返回值是一个数组,如果没有捕获到返回值为null
- ["2", index: 3, input: "abc23", groups: undefined]
- 0: "2" 捕获到内容
- index: 3 捕获到的起始索引位置
- input: "abc23" 原始字符串
- groups 命名匹配
index、groups、input都是是正则捕获到的数组的自带属性,这个数组的长度为1
2.2 正则的懒惰性
每次调用正则捕获或者匹配的时候都是从索引0处开始捕获的
let reg = /\d+/
let str = 'abc111 abc222 abc333'
reg.exec(str)// ["111", index: 3, input: "abc111 abc222 abc333", groups: undefined]
reg.exec(str)// ["111", index: 3, input: "abc111 abc222 abc333", groups: undefined]
reg.exec(str)// ["111", index: 3, input: "abc111 abc222 abc333", groups: undefined]
取消正则的懒惰性,修饰符g(global),会从上一次匹配或者捕获成功的位置继续向后查找
如果捕获到结束边界 会从头再来
let reg = /\d+/g
let str = 'abc111 abc222 abc333'
console.log(reg.exec(str))
// ["111", index: 3, input: "abc111 abc222 abc333", groups: undefined] test:true
console.log(reg.exec(str))
// ["222", index: 10, input: "abc111 abc222 abc333", groups: undefined] test:true
console.log(reg.exec(str))
// ["333", index: 17, input: "abc111 abc222 abc333", groups: undefined] testLtrue
console.log(reg.exec(str))
// null test:false
console.log(reg.exec(str))
// ["111", index: 3, input: "abc111 abc222 abc333", groups: undefined] test:true
2.3 正则实例下的属性
lastIndex:他是正则实例上的一个属性,下一次开始查找的起始索引
lastIndex的值可以手动修改,但是如果没有修饰符g的时候,修改不会起作用
reg.lastIndex = 9
无论是test还是exec还是混用,当加修饰符/g时,lastIndex属性都是起决定性的因素。
let reg = /\d+/
reg.exec('abc111 abc222 abc333') //111 index=3
reg.exec('a444 b555 c666') //555
因为lastIndex是正则实例对象reg上的属性,换字符串并不会影响他下面私有属性的值
global属性是用来标识正则是否有g修饰符的,返回值时true和false
multline属性是否支持多行匹配
ignoreCase属性是否忽略大小写
sourse正则所有的元字符内容,不包括外界的两个斜杠
2.4 字符串的match方法
match支持正则,在他的底层封装的还是exec,当没有取消正则懒惰性的情况下,他的返回值和exec相同,如果匹配不到返回值为null
如果取消了正则的懒惰性,match会以数组的形式将捕获到的结果一次性返回
2.5 正则的贪婪性
正则的贪婪性:每一次捕获的时候总是捕获到满足当前正则的最长内容
取消正则的贪婪性:量词后面加?,按照最短的情况进行匹配
let reg = /\d{2,8}?/g
2.6 分组捕获
数组返回的第一项始终是整个大正则捕获到的内容,如果有分组,会从第二项开始依次排列
let reg = /hello(\d+)/g
let str = 'hello2018 abc111 hello2019 abc222'
console.log(reg.exec(str))
// ["hello2018",2018, index: 0, input: "hello2018 abc111 hello2019 abc222", groups: undefined]
console.log(reg.exec(str))
// ["hello2019",2019, index: 0, input: "hello2018 abc111 hello2019 abc222", groups: undefined]
如果不加g,由于match的底层就是exec,所以返回值和exec相同
如果加了g,由于match是一次性捕获,所以不能捕获分组内容的
取消分组
在小括号的前面加上一个(?:)只匹配,不捕获
let reg = /^(?:start|end)$/
2.7 分组引用
let reg = /^1(\d)\1$/
\1是对分组1的再次引用,这里存在几个小问题,其一是引用的分组表示两个分组的内容相同,第二个是通过引用的分组通过RegExp.$拿不到
let reg = /^([a-z])\1([a-z]\1)$/
//aabb ccdd
2.8 正向预查、负向预查
正向预查(?=元字符) 不会发生分组捕获
负向预查(?!元字符) 不会发生分组捕获
// 正向预查
// 捕获 后面跟着3或者4的字母
let reg1 = /[a-z](?=3|4)/g
let str1 = 'a1 b2 c3 d4'
console.log(str1.match(reg1)) //["c", "d"]
// 负向预查
// 捕获 后面跟着的不是3或者4的字母
let reg2 = /[a-z](?!3|4)/g
let str2 = 'a1 b2 c3 d4'
console.log(str2.match(reg2)) // ["a", "b"]
捕获的不是预查的内容而是范围内的内容
正向预查或者负向预查如果放在开头的话,作用是限定后面的范围\d,\w,[]
let reg11 = /^((?=3)\d)+$/
console.log(reg1.test('3333')) // true限定数字只能为3
let reg2 = /^((?!4)\d)+$/g
console.log(reg2.test('1123123')) // true 限定数字里不能有4
console.log(reg2.test('11234123')) // false
console.log(reg2.test('11234566')) // false
2.9 m修饰符(了解)
let reg = /^a.+b$/
let str = `a1b
a2b
a3b
`
reg.test(str)//false 因为没有以a为开头,b为结尾的
let reg1 = /^a.+b$/m
reg1.exec(str)//a1b 会把每一行都看作独立的一行进行匹配
reg1.exec(str)//a2b
reg1.exec(str)//a3b
2.10 字符串的replace、match、split都可以使用正则表达式
split
// let sss = '1a2b3c4'
// console.log(sss.split(/[a-z]/)) // [1, 2, 3, 4]
replace
replace的底层封装的也是exec,相当于自动的对exec进行遍历
let str = 'aaa2018 bbb2019'
let reg = /hello/ //如果加g,匹配到多少次,这个函数就会被执行几次
//他会把exec捕获到的值当做参数传递给这个函数
//这个函数的返回值就是replace捕获到的内容所要替换的值
str.replace(reg,(...rest)=>{
console.log(rest)
return 'AA'
})
这个函数的返回值就是匹配到的内容要替换的新内容,有点类似于数组的map方法
let str2 = str.replace(/\d\b/g, a => a * 2)
//第一个参数是正则捕获到的内容,由于没有用扩展运算符,所以穿入的直接是变量而不是数组
3.正则表达式的应用
3.1 时间字符串
方法一
let reg = \(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\
str.replace(reg,`$1年$2月$3日 $4时$5分$6秒`)
str.replace(reg,function(...rest){
return `${rest[1]}年${rest[2]}月${rest[3]}日`
})
方法二
let time2 = time.split(/[-\s:]/)//[2019,20,1,11,56,56]
let text = ['年','月','日',' ','时','分','秒']
//forEach拼接
方法三
let ary = time.match(/\d+/g)
let text = ['年','月','日',' ','时','分','秒']
ary.map()//遍历修改
3.2 统计字符串中字母出现的次数
let newStr = str.split('').sort().join('')
let reg = /(\w)\1*/g
let obj = {}
newStr.replace(reg,function(){
obj[rest[1]] = obj[rest[0].length]
})
3.3 获取url中的参数
let reg = /[?&]([^?&=]+)=([^?&=]+)/g
3.4 千分符
function format (num) {
var reg=/\d{1,3}(?=(\d{3})+$)/g;
return (num + '').replace(reg, '$&,');
}
3.5 匹配手机号
let reg = /^1(36|58)\d{8}$/
3.6 验证有效数字
let reg = /^[+-]?(\d|[1-9]\d+)(\.\d+)?$/
3.7 验证范围内数字23~66
拆分为三段23-29,30-59,60-66
let reg = /(2[3-9])|([3-5]\d)|(6[0-6])/
3.8 验证邮箱
第一部分可以为\w和. - ,但是. -不能作为开头和结尾i,不能连续
第二部分域名只能为数字字母
let reg = /^\w+([-.]\w+)*@[\da-zA-Z]{2,6}(\.[a-z]{2,6}){1,2}$/
3.9 验证类型
function testType(data){
let str = Object.prototype.tostring.call(data)
let reg = /\[object (\w+)\]/
return str.exec(reg)[1].toLowerCase()
}