用来处理字符串的规则
- 只能处理字符串
- 它是一个规则:可以验证字符串是否符合某个规则(test),也可以把字符串中符合规则的内容捕获到(exec/match…)
let str = 'good'
let reg = /\d+/
reg.test(str) //false 不包含数字
str = '2021-09-10'
reg.exec(str) //['2021', index: 0, inputs: '原始字符串']
复制代码
编写正则表达式
创建方式有两种
// 1. 字面量创建方式(两个斜杠之间的,都是用来描述规则的元字符)
let reg1 = /\d+/
// 2. 构造函数模式创建,有两个参数:元字符字符串,修饰符字符串
let reg2 = new RegExp('\\d+')
//当需要把变量加入到正则表达式中时,不能使用字面量的方式创建,只能用构造函数去创建
复制代码
正则表达式由两部分组成
- 元字符
- 修饰符
常用的元字符
// 1.量词元字符:设置出现的次数
* 零到多次
+ 一到多次
? 零次或者一次
{n} 出现n次
{n, } 出现n到多次
{n, m} 出现n到m次
// 2.特殊元字符:单个或者组合在一起代表特殊的含义
\ 转义字符(普通->特殊->普通)
. 除\n(换行符)以外的任意字符
^ 以哪一个元字符作为开始
$ 以哪一个元字符作为结束
\n 换行符
\d 0~9之间的一个数字
\D 非0~9之间的一个数字
\w 数字、字母、下划线中的任意一个字符
\W 非数字、字母、下划线中的任意一个字符
\s 一个空白字符(包含空格、制表符、换页符等)
\t 一个制表符(一个TAB键:四个空格)
\b 匹配一个单词的边界
x|y x或者y中的一个字符
[xyz] x、y、z中的一个字符
[^xy] 除了xy以外的任意一个字符
[a-z] 指定a-z这个范围中的任意一个字符 [0-9a-zA-Z_] === \w
[^a-z] 除了a-z这个范围中的任意一个字符
() 正则中的分组符号
(?:) 只匹配不捕获
(?=) 正向预查
(?!) 负向预查
// 3.普通元字符:代表本身的含义
/js/ 此正则匹配的就是 "js"
复制代码
常用修饰符:i m g
i: ignoreCase 忽略单词大小写匹配
m: multiline 可以进行多行匹配
g: global 全局匹配
/A/.test('lalalala') //false
/A/i.test('lalalala') //true
复制代码
元字符详细解析
^ $
let reg = /^\d/ //必须以数字开头
console.log(reg.test('js')) //false
console.log(reg.test('2021js')) //true
console.log(reg.test('js2021')) //false
reg = /\d$/ //必须以数字结束
console.log(reg.test('js')) //false
console.log(reg.test('2021js')) //false
console.log(reg.test('js2021')) //true
//^ $两个都不加:字符串中包含符合规则的内容即可
reg = /\d+/:字符串中包含1到多个数字即可
//^ $两个都加:字符串只能是和规则一致的内容
reg = /^\d+$/
//验证手机号(11为,以1开头)
reg = /^1\d{10}$/
复制代码
\
let reg = /^2.3$/ // .不是小数点,是\n外的任意字符
reg.test('2.3') //true
reg.test('2@3') //true
reg.test('23') //false
reg = /^2\.3$/ //只能代表2.3了
//匹配"\d"
let str = "\\d" //在字符串中\也有特殊含义
reg = /^\d$/ //不能匹配str
reg = /^\\d$/
reg.test("\\d") //正确匹配str
复制代码
x|y
let reg = /^18|29$/
reg.test('18')
reg.test('29')
reg.test('129')
reg.test('189')
reg.test('1829')
reg.test('829')
reg.test('182')
//以上结果全为true
//------直接x|y会存在很乱的优先级问题,可以用小括号处理 => 小括号:分组
reg = /^(18|29)$/ //只能是18或者29
复制代码
[]
// 1. 中括号中出现的字符一般都代表字符本身的含义
let reg = /^[@+]+$/ //@或者+ 出现一到多次
reg = /^[\d]$/ // \d在[]中代表0-9的一个数字
reg = /^[\\d]$/ // \ 或者 d
// 2. 中括号中不存在多位数
reg = /^[18]$/ //只能匹配1或者8,不能匹配18
reg = /^[10-29]$/ // 1或者0-2或者9
复制代码
常用正则表达式
- 验证是否为有效数字
/*
* 规则分析
* 1. 可能出现 + - 号,也可能不出现
* 2. 一位0-9都可以, 多位时第一位不能是0
* 3. 小数部分可能有可能没有,一旦有小数点,小数点后面一定有数字
*/
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
复制代码
- 验证密码
// 数字、字母、下划线 6——16位
let reg = /^\w{6, 16}$/
let flag = reg.test('密码字符串')
复制代码
- 验证真实姓名
/* 1. 只能是中文汉字 /^\u4E00-\u9FA5$/
* 2. 名字长度 2-10位
* 3. 可能有译名 ·汉字 (爱新觉罗·康熙)
*/
let reg = /^[\u4E00-\u9FA5]{2, 10}(·[\u4E00-\u9FA5]{2, 10}){0, 2}$/
复制代码
- 验证邮箱
let reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/
// => \w+((-\w+)|(\.\w+))*
// 1. 开头是数字字母下划线(1到多位)
// 2. 还可以是 -数字字母下划线 或者 .数字字母下划线,整体0到多次
// => 邮箱的名字由"数字、字母、下划线、-、."几部分组成,但是-/.不能连续出现也不能作为开始
// => @[A-Za-z0-9]+
// 1. @后面紧跟着:数字、字母(1到多位)
// => ((\.|-)[A-Za-z0-9]+)*
// 1. 对@后面名字的补充 多域名 .com.cn
//企业域名 @aaa-bbb-ccc-ddd.com
// => \.[A-Za-z0-9]+
// 匹配最后的域名 .com(cn, org, edu, net, vip...)
复制代码
- 身份证号码
/*
* 1. 一共18位
* 2. 最后一位可能是X
* 身份证前6位: 省市县 511623
* 中间8位: 年月日
* 最后4位:
* 最后一位 => X 或者 数字
* 倒数第二位 => 偶数:女 奇数:男
*/
// => 小括号分组的第二个作用:分组捕获,不仅可以把大正则匹配的信息捕获到,还可以单独捕获到每个小分组的内容
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/
reg.exec("511623199006141110")
//["511623199006141110", "511623", "1990", "06", "10", "1", "0", ...] 捕获结果是数组,数组第一位是捕获到的完整的正则匹配到的信息,后面的是捕获到的小括号中匹配到的信息
复制代码
正则的捕获
实现正则捕获的方法
- 正则RegExp.prototype上的方法
- exec
- test
- 字符串String.prototype上支持正则表达式处理的方法
- replace
- match
- splite
- …
基于exec实现正则的捕获
- 捕获到的结果是null或数组
- 第一项:本次捕获到的内容
- 其余项: 对应小分组本次单独捕获的内容
- index:当前捕获内容在字符串中的起始索引
- input: 原始字符串
- 每执行一次exec,只能捕获到一个符合正则规则的,但是默认情况下,我们执行100遍,获取的结果永远是第一个匹配到的,其余的捕获不到,这就是
正则捕获的懒惰性
:默认只捕获第一个。
let str = 'abcde123fgh456igk789lmn'
let reg = /\d+/
//reg.lastIndex: 当前正则下一次匹配的起始索引位置,这个值不会被修改,所以匹配到的永远是第一个
console.log(reg.lastIndex) //0 下面的匹配捕获是从str索引0的位置开始
//正则捕获的前提:当前正则要与字符串匹配。不匹配,捕获结果为null
console.log(reg.exec(str)) //["123", index: 5, input: "abcde123fgh456igk789lmn", groups: undefined]
reg = /\d+/g //设置全局匹配修饰符后,每一次匹配完,lastIndex会自动修改,多次捕获后,当全部捕获后,再次捕获结果为null,lastIndex回归为初始值0,再次捕获又从第一个开始
if(reg.test(str)){ //这里匹配后,已经修改了lastIndex的值
console.log(reg.exec(str)) //456 这里捕获的是第二个结果
}
//字符串中的match方法,可以在执行一次的情况下,捕获到所有匹配的数据(前提:正则设置了g)
console.log(str.macth(reg)) //["123", "456", "789"]
复制代码
正则的分组捕获
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/
reg.exec("511623199006141110")
//["511623199006141110", "511623", "1990", "06", "10", "1", ...] 捕获结果是数组,数组第一位是捕获到的完整的正则匹配到的信息,后面的是捕获到的小括号中匹配到的信息
//最后一项,加了()只是为了改变|的优先级,但是并不需要捕获,可以基于?:来处理,代表只匹配不捕获
复制代码
//既要匹配到{数字},也想单独匹配带数字 {0} 0...
let str = "{0}年{1}月{2}日"
//不设置g只匹配一次,exec和match获取的结果一致(既有大正则匹配的信息,也有小分组匹配的信息)
let reg = /\{(\d+)\}/ // ["{0}", "0", index: 0, input: "{0}年{1}月{2}日", groups: undefined]
let reg = /\{(\d+)\}/g
console.log(str.match(reg)) // ["{0}", "{1}", "{2}"] 全局匹配的情况下,match只能把大正则匹配的内容捕获到,小分组匹配的信息无法获取
let aryBig = [],
arySmall = [],
res = reg.exec(str) //用exec一次一次获取
while (res) { // 循环到res = null为止
let [big, small] = res
aryBig.push(big)
arySmall.push(small)
res = reg.exec(str) //没有捕获完就一直捕获
}
console.log(aryBig) //["{0}", "{1}", "{2}"]
console.log(arySmall) //["0", "1", "2"]
复制代码
分组的第三个作用:分组引用
分组引用就是通过”\数字”让其代表和对应分组出现一模一样的内容
let str = "book" //要求一共4个字母,中间两个字母一样
let reg = /^[A-Za-z]([A-Za-z])\1[A-Za-z]$/
console.log(reg.test(str))
复制代码
正则捕获的贪婪性
默认情况下,正则捕获时,是按照当前正则所匹配的最长结果来获取的
在量词元字符后面设置
?
, 代表取消捕获时的贪婪性(按照正则匹配的最短结果来获取)
let str = 'aaa1111@2222bbb'
let reg = /\d+/g
console.log(str.match(reg)) //["1111", "2222"]
reg = /\d+?/g //["1", "1", "1", "1", "2", "2", "2", "2"]
复制代码
-
?
在正则中的五大作用:- 左边是普通元字符,本身代表量词元字符出现0或者1次
- 左边是量词元字符,代表取消捕获时候的贪婪性
- (?:) 只匹配不捕获
- (?=) 正向预查
- (?!) 负向预查
-
()
在正则中的三大作用- 改变优先级
- 分组捕获
- 分组引用 \数字
其他正则捕获的方法
- test也能捕获(本意是匹配)
let str = "{0}年{1}月{2}日"
let reg = /{(\d+)}/g
console.log(reg.test(str)) //true
console.log(typeof 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 = 'abc000abc111' //把abc替换成xyz
//不用正则,执行一次只能替换一个
//str = str.replace('abc', 'xyz').replace('abc', 'xyz')
//console.log(str) //xyz000xyz111
str = str.replace(/abc/g, 'xyz')
//console.log(str) //xyz000xyz111
复制代码
let str = 'abc000abc111' //把abc替换成abcxyz
str = str.replace('abc', 'abcxyz').replace('abc', 'abcxyz') //每一次捕获都是从字符串第一个位置开始找,类似于正则的懒惰性
console.log(str) // abcxyzxyz000abc111
str = str.replace(/abc/g, 'abcxyz')
console.log(str) // abcxyz000abcxyz111
复制代码
let time = '2021-09-11' //变成"2021年09月11日"
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/ //注意 {1,2} 逗号后面不能加空格,否则会匹配失败
console.log(reg.test(time)) //true
time = time.replace(reg, "$1年$2月$3日")
console.log(time) //2021年09月11日
复制代码
通过上面的三小段代码,我们可看出replace方法有下面这三种特性:
1. 首先拿reg和time进行匹配,能匹配几次,就把传递进来的函数执行几次(而且匹配一次就执行一次)
2. 不仅执行了方法,replace还给方法传递了实参(和exec捕获的内容一致的信息,大正则匹配的内容,小分组匹配的信息)
3. 在函数中我们返回的是啥,就把当前大正则匹配的内容替换成啥。
复制代码
let time = '2021-09-11' //变成"2021年09月11日"
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/
time = time.replace(reg, (...arg) => {
let [, $1, $2, $3] = arg
return $1 + '年' + $2 + '月' + $3 + '日'
})
console.log(time) //2021年09月11日
复制代码
单词首字母大写
//单词首字母大写
let str = 'good good study, day day up!'
let reg = /\b([a-zA-z])[a-zA-z]*\b/g
str = str.replace(reg, (x, y) => {
return y.toUpperCase() + x.slice(1)
})
console.log(str) //Good Good Study, Day Day Up!
复制代码
验证一个字符串中哪个字母出现的次数最多,有多少次
let str = 'jintianshixingqiliu'
let arr = str.split('')
arr = [...new Set(arr)]
let newArr = []
arr.forEach(item => {
let tmp = {
code: item
}
let i = 0
str = str.replace(new RegExp(item + "{1}", 'g'), () => i++)
tmp['num'] = i
newArr.push(tmp)
})
newArr.sort((a, b) => b.num - a.num)
console.log(newArr[0]) //{code: "i", num: 6}
复制代码
let str = 'jintianshixingqiliu'
str = str.split('').sort((a, b) => a.localeCompare(b)).join('') //排序
let reg = /([a-zA-Z])\1+/g //匹配有相邻的相同的子字符串
let ary = str.match(reg)
ary.sort((a, b) => b.length - a.length)
console.log(ary[0].slice(0, 1), ary[0].length) //i, 6
let max = ary[0].length,
res = [ary[0].slice(0, 1)] // //可能有相同的最大数的字母
for (let i = 1; i < ary.length; i++) {
let item = ary[i]
if (item.length < max) {
break
}
res.push(item.slice(0, 1))
}
console.log(res)
复制代码
let str = 'jintianshixingqiliu',
res = [],
max = 0,
flag = false //是否已经找到
str = str.split('').sort((a, b) => a.localeCompare(b)).join('')
for (let i = str.length; i > 0; i--) {
let reg = new RegExp("([a-zA-Z])\\1{" + (i - 1) + "}", "g")
str.replace(reg, (content, $1) => {
res.push($1)
max = i
flag = true
})
if (flag) break
}
console.log(res, max) //["i"] 6
复制代码
千分符
(function () {
function millimeter() {
return this.replace(/\d{1,3}(?=(\d{3})+$)/g, (content) => {
return content + ','
})
}
['millimeter'].forEach(item => {
String.prototype[item] = eval(item)
})
})();
let num = '2645565264562'
console.log(num.millimeter()) //2,645,565,264,562
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END