背景
正则表达式一直是困扰很多程序员的一门技术。曾经在我的眼里,正则表达式就是一堆杂乱无章的符号,他不认识我,我也不认识他。再加上正则几乎很少出现在面试的过程中,所以对于正则我们也是更加的陌生。在实际的开发过程中需要用到正则的时候打谷歌直接搜索拷贝,甚至找到的正则都卡不懂。有人也会说,不用正则,对于我们的开发也没什么问题呀,为什么还要学这些奇奇怪怪的字符呢?那么我们来看一个小小的案例:
验证密码,规则
- 数字、字母、下划线
- 6~16位
如果不用正则我们可能写出如下的方法:
const password = 'zhuanzhuan0624'
function checkPass(val) {
if (val.length < 6 || val.length > 16) {
alert('长度必须介于6-16位之间')
return
}
const area = ['a’', 'b', 'c', 'd','_'] // 包含数字、字母、下划线,此处只列举的一部分
for (let i = 0; i < val.length; i++) {
const chat = val[i];
if (!area.includes(chat)) {
alert('您的输入存在非法字符')
return
}
}
}
checkPass(password)
那如果我们使用了正则呢?
const password = 'zhuanzhuan0624'
const passwordReg = /^\w{6,16}$/
console.log(passwordReg.test(password));
这是开发中最常用的正则之一吧,除此之外还有用户名、解析url、千分位、有效数字判断等等,文末有大量常用正则!
有人就不服了,正则就这吗,能帮到我们面试吗?行,那再来看一到面试题:
获取字符串中出现次数最多的字母和次数
解题的方式有很多,相信大家都已经很熟悉了,那么我们直接看看用正则怎么处理?
function checkStr(str) {
// 排序
const newStr = str.split('').sort((a,b) => a.localeCompare(b)).join('')
// 分组引用
const reg = /([a-zA-Z])\1+/g
return newStr.match(reg).sort((a,b) => b.length -a.length)[0]
}
console.log(checkStr('aaabbdddddxxfrr')); // ddddd
除此之外还有很多,比如:
就不再这里一一列举了。正则是一门艺术,我相信深入理解正则表达式并能融会贯通是很有意义的,对自己的提升也是很大的。当你学完正则再去看vue解析模版时候使用的正则表达式就很轻松了,也可以让我们更好的理解代码。当下次再遇到正则表达式的时候可以不借助搜索引擎,自己解决。
什么是正则
当我学习完正则以后,我自己对于正则的理解:
一种处理字符串的规则,验证字符串是否符合某种规则
那么规则怎么来呢?规则由特定的符号进行组合,组合的结果就是正则表达式。 在我心里就把他们当作符号就行了,每个符号代表不同的含义,我们只要记住常用符号的含义,然后就可以可以自由组合成不同的规则了。
那么正则都有哪些种类的符号呢?简单的进行一下分类:
- 元字符:简单的说就是写在
/ /
之间的符号- 量词元字符
- 特殊元字符
- 修饰符:控制正则执行的行为
其实我们要记忆的东西也不是很多,接下来深入了解一下常见的符号(元字符和修饰符)。
元字符
量词元字符
设置出现的次数
*
零到多次+
一到多次?
零次或者一次{n}
出现n次{n,}
出现n到多次{n,m}
出现n到m次
特殊元字符
单个或者组合在一起代表特殊的含义
\
转义字符(普通->特殊->普通).
除\n (换行符)以外的任意字符^
以哪一个元字符作为开始$
以哪一个元字符作为结束\n
换行符\d
0~9之间的一个数字\D
非0~9之间的一个数字(大写 和小写的意思是相反的)\w
数字、字母、下划线中的任意一个字符\s
一个空白字符(包含空格、制表符、换页符等)\t
一个制表符(一个TAB键:四个空格)x|y
x或者y中的一个字符[xyz]
x或者y或者z中的一个字符[^xy]
除了x/y以外的任意字符[a-z]
指定a-z这个范围中的任意字符[^a-z]
上一个的取反“非”\b
匹配一个单词的边界()
正则中的分组符号(?:)
只匹配不捕获(?=)
正向预查(?!)
负向预查
以上的元字符是我们最常见的,也不多,共26个,一会写几个案例,不用背,就记下来了!
关注微信公众号【WEB大前端】,也可在文末进行扫码,回复【正则元字符】,可免费获取完整的元字符及其含义列表的PDF文件
修饰符
i
忽略单词大小写匹配g
全局匹配m
可以进行多行匹配
修饰符相比于元字符就少很多啦,接下来先练习一下元字符!
元字符练习
^ $
^: 以哪一个元字符作为开始 $: 以哪一个元字符作为结束,
// 匹配以数字开头
const reg = /^\d/
console.log(reg.test("zhuanzhuan")) // false
console.log(reg.test("2022,I love zhuanzhuan more")) // true
console.log(reg.test("I love zhuanzhuan more, 2022")) // false
// 匹配以数字结尾
const reg = /\d$/
console.log(reg.test("zhuanzhuan")) // false
console.log(reg.test("2022,I love zhuanzhuan more")) // false
console.log(reg.test("I love zhuanzhuan more, 2022")) // true
// ^$ 两个都不加:字符串中包含符合规则的内容即可
const reg = /\d/
console.log(reg.test("zhuanzhuan")) // false
console.log(reg.test("2022,I love zhuanzhuan more")) // true
console.log(reg.test("I love zhuanzhuan more, 2022")) // true
// ^$ 两个都加:字符串只能是和规则一致的内容
const reg = /^\d$/
console.log(reg.test("zhuanzhuan")) // false
console.log(reg.test("2022,I love zhuanzhuan more")) // false
console.log(reg.test("I love zhuanzhuan more, 2022")) // false
console.log(reg.test("2022")) // false, 思考? 匹配 2022 的正则表达式应该怎么写呢?
console.log(reg.test("2")) // true
// 简单的案例:验证手机号码(11位,第一个数字是1即可, 此处只做简单的案例学习使用)
const reg = /^1\d{10}$/
console.log(reg.test("18372635819")) // true
console.log(reg.test("48372635819")) // false
\
转义字符(普通->特殊->普通)
const str = '\\d'
const reg1 = /^\d$/
console.log(reg1.test(str)); // false 此处的 \d 表示 0-9的数字
const reg2 = /^\\d$/ // 把特殊符号转换为普通符号
console.log(reg2.test(str)); // true
const str2 = '\d'
console.log(reg2.test(str2)); // false why? 字符串中的\ 也需要转译, 这里可以先记住,进阶内容中会有相关说明
x|y
x或者y中的一个字符
const reg = /^18|37$/
console.log(reg.test("18")) // true
console.log(reg.test("37")) // true
console.log(reg.test("137")) // true
console.log(reg.test("189")) // true
console.log(reg.test("1837")) // true
console.log(reg.test("837")) // true
console.log(reg.test("182")) // true
// 直接x|y会存在很乱的优先级问题,一般我们写的时候都伴随着小括号 进行分组,因为小括号改变处理的优先级 小括号:分组
const reg2 = /^(18|37)$/
console.log(reg2.test("18")) // true
console.log(reg2.test("37")) // true
console.log(reg2.test("137")) // fa1se
console.log(reg2.test("189")) // false
// 只能是18或者37中的一个了
[]
1.中括号中出现的字符一般都代表本身的含义
const reg = /^[@+]$/ // 此处的 + 就是+本身,并不代表 一到多次
console.log(reg.test("@")) // true
console.log(reg.test("+")) // true
console.log(reg.test("@@")) // false
console.log(reg.test("@+")) // false
const reg2 = /^[\d]$/ // \d在中括号中还是0-9
console.log(reg2.test("d")) // false
console.log(reg2.test("\\")) // false
console.log(reg2.test("9")) // true
2、中括号中不存在多位数
const reg = /^[18]$/
console.log(reg.test("1")) // true
console.log(reg.test("8")) // true
console.log(reg.test("18")) // false
const reg2 = /^[10-49]$/ // 1或者0-4或者9
console.log(reg2.test("1")) // true
console.log(reg2.test("9")) // true
console.log(reg2.test("0")) // true
console.log(reg2.test("2")) // true
console.log(reg2.test("10")) // false
console.log(reg2.test("3")) // true
console.log(reg2.test("20")) // false
认真理解这几个元字符对我们下面的案例很重要!
小试牛刀
正则就是规则,那么先搞清楚需要匹配什么字符串,规则是什么?
验证是否为有效数字
/**
* 规则分析
* 1.可能出现 + - 号,也可能不出现, 对应正则: [+-]?
* 2.一位0-9都可以,多位首位不能是0 ,对应正则: (\d|([1-9]\d+))
* 3.小数部分可能有可能没有,一旦有后面必须有小数点+数字,对应正则: (\.\d+)?
*/
const effectiveNum = [0, '+2022', '-2022', '0.2' ,'-2.2' , '3.1415926']
// 将上述的规则进行合并,就可以得到完整的正则表达式
const reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
effectiveNum.forEach( num => console.log(reg.test(num)) )
验证密码
/**
* 规则分析
* 1、数字、字母、下划线, 对应正则: \w
* 2、6~16位, 对应正则: {6,16}
*/
// 将上述的规则进行合并,就可以得到完整的正则表达式
const reg = /^\w{6,16}$/
const pwd1 = 'zhuanzhuan0624'
const pwd2 = 'z0624'
console.log(reg.test(pwd1)) // true
console.log(reg.test(pwd2)) // false
验证用户名
/**
* 规则
* 1、汉字, 对应正则: /^[\u4e00-\u9fa5]$/
* 2、名字长度 2~10位, 对应正则: {2,10}
* 3、可能有译名.汉字 /(·[\u4e00-\u9fa5]{2,10}){0,2}/
*/
// 将上述的规则进行合并,就可以得到完整的正则表达式
const nameReg =/^[\u4e00-\u9fa5]{2,10}(·[\u4e00-\u9fa5]{2,10}){0,2}$/
const name1 = '弗拉基米尔·弗拉基米罗维奇·普京'
const name2 = '杨大仙'
console.log(nameReg.test(name1)) // true
console.log(nameReg.test(name2)) // true
邮箱
const reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
const email = '1806328384@qq.com'
console.log(reg.test(email))
=> \w+((-\w+)|(.\w+))*
1.开头是数字字母下划线(1到多位)
2.还可以是-数字字母下划线或者. 数字字母下划线,整体零到多次
=> 邮箱的名字由“数字、字母、下划线、-、. ”几部分组成,但是-/.不能连续出现也不能作为开始
=> @[A-Za-z0-9]+
- @后面紧跟着:数字、字母(1-多位)
=> ((.|-)[A-Za-z0-9]+)*
1.对@后面名字的补充
多域名 .com.cn
=> .[A-Za-z0-9]+
- 这个匹配的是最后的域名 (.com/.cn/.org/.edu/.net... )
身份证
规则:
1、一共18位 2、最后一位可能是X 3、身份证前六位:省市县 4、中间八位:年月日 5、最后四位:
- 最后一位=> X或者数字
- 倒数第二位=>偶数女奇数男
- 其余的是经过算法算出来的
// 小括号分组的第二个作用:分组捕获,不仅可以把大正则匹配的信息捕获到,还可以单独捕获到每个小分组的内容。 下一篇文档会讲到,此时可以先略过
const reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
console.log(reg.exec("42068419960526751X"))
// [
// '42068419960526751X',
// '420684',
// '1996',
// '05',
// '26',
// '1',
// 'X',
// index: 0,
// input: '42068419960526751X',
// groups: undefined
// ]
idea:目前已经拿到身份证的相关信息,如果我们获取到前六位的所有的数据信息,是不是就可以做一个身份证查询的接口喽!
总结
这里我们已经接触到了捕获,也是到了这篇文章的尾声,相信读到这里,对于正则已经不在恐惧了吧,算是已经入了个门。细细品尝,其实正则也很好玩,下一篇会介绍正则的捕获、贪婪、懒惰及相关的原理,下节更精彩!最后留一个 “做人不能太正则!” ,谜底下篇揭晓。
本文属于个人理解,如有误,请斧正!
大量的正则表达式: any86.github.io/any-rule/
——本文首发于个人公众号,转载请注明出处———
最后,欢迎大家关注我的公众号,一起学习交流。