最近发现很多人对正则表达式一知半解,希望能通过这个教程能给你带来一些帮助。
1.正则表达式用来做什么
现在有一个场景,产品给张三提了一个需求:
产品:我需要一个输入框,这个输入框用来输入用户的昵称,昵称的最多可以输入6个汉字
张三:这简单 <input maxlength=6/>
产品:不可以输入空格等特殊字符
张三:String.prototype.replaceAll(' ','')
产品:不可以输入不文明用语
张三:String.prototype.indexOf('fuck')!==-1
产品:可以输入英文,最长12个字符
张三:你为什么不直接说最大12个字节!!!这个需求做不……
张三愤怒转身看到产品突然手里一个方方正正又有点长的固态物体,咽了咽口水,说这个需求做不好我就不下班!!!
旁边的小明听了,笑着说:张三你又要加班啊
张三吞吞吐吐:程序员的事怎么能叫加班呢
接下来便是一些听不懂的话,什么晨兴写代码,带月荷机归,办公室顿时充满了欢快的气氛。
聪明的你现在一定想到了这不正是正则表达式的用武之地吗。没错 正则表达式就是设置一个字符串的规则,他可以判断字符串是否符合正则表达式的规则,也可以匹配字符串中符合规则的部分子串。
正则表达式十分强大,但使用的同时我们需要知道他的优缺点
优点:
1.可以用少量的代码来匹配复杂的内容
2.大多情况下正则表达式的时间复杂度为o(n)
缺点:
- 较差可读性带来较大的修改难度
- 一旦产生回溯性能较差
2. 正则表达式长什么样?
正则表达式由两个斜杠和斜杠之间的规则以及第二个斜杠之后的属性描述符组成,例如:
/a{2,3}/g
这个正则表达式就会匹配字符串中连续出现的a字符2次到3次部分。
正则表达式中用一些特殊的字符来表示某些字符的集合,这里我们介绍一些常用的:
(1) []中括号用来表示一个匹配的字符集中的匹配字符,在中括号中出现的内容都是||或关系。如:/[abc]/他可以匹配字符串中的a或b或c。他还有以下用法:
/[A-z]/ 指定一个字母范围进行匹配。他将匹配从大写的A到小写的z等52个英文字母也可以/[A-E]/匹配A、B、C、D、E五个字母。
/[0-9]/指定一个数字范围进行匹配。他将匹配从0到9的10个字符,也可以用\d来代表[0-9]匹配数字。如:/\d/{3}用来配位3位数字。
/[\u4e00-\u9fa5]/指定一个Unicode字符集范围进行匹配。他将匹配从字符集中从\u4e00到\u9fa5等字符。
/[^a-f]/指定一个字母范围,匹配不是这个字母范围的内容,这里的^是取反操作,表示匹配非a到f的字母。
(2) {} 花括号用来表示连续匹配次数,如:/[abc]{3,5}/ 他可以匹配abc中任意字符连续出现3到5次,如abc、acc、aaaaa都符合abc中任意字符连续出现3到5次。
除了上面的例子他有如下几种写法:
/[abc]{3}/ 表示固定连续匹配 a-c三次
/[abc]{3,}/表示至少连续匹配3次
/[abc]*/ *表示匹配0次或多次,等同于/[abc]{0,}/
/[abc]?/ 表示匹配0次或1次,等同于/[abc]{0,1}/
/[abc]+/ 表示匹配1次或多次,等同于/[abc]{1,}/
*、?、+虽然没用花括号,但作用是一样的
(3) ()小括号被称为捕获括号,他会匹配括号中的规则并记录,用来获取字符串中的符合规则的子串
(4)^ 匹配以正则表达式规则开头的字符串。如:/^a/匹配以a开头的字符串 abc,在[]中出现则表示取反操作,如/[^a]/则表示匹配不是a的字母,千万不要和在[]外的^搞混。
(5)$匹配以正则表达式规则结尾的字符串。如:/js\$/匹配以js结尾的字符串如index.js。配合^使用,/^abc$/匹配以a开头,中间为b,以c结尾的字符串。
(6)属性描述符
属性描述符写在第二个斜线之后/a/g,他用来描述正则表达式的匹配模式常用的有:
g 表示全局匹配,例如/a/g将匹配字符串中所有的a
i 表示忽略大小写,例如/a/i 也可以匹配大写的A如:aAA
也可以连续使用几个描述符如/^a{3}$/gi他与aAa是匹配的
3.先行断言、后行断言、正向否定查找、反向否定查找
(1)先行断言
/a(?=b)/ 这是一个简单的先行断言,当a后面的值为b时,匹配a。如abc,则匹配a,acc则不匹配a。
小明让张三去买个西瓜,嘱咐他,如果西瓜上画着喜羊羊,那就买。如果没画着喜羊羊就不买,因为小明喜欢画着喜羊羊的西瓜。这就是一个先行断言,首先要找到西瓜,然后西瓜上必须画着喜羊羊。
我们的目的是西瓜,西瓜是我们最终想要的结果,而画着喜羊羊是用来描述西瓜的其他特征,是西瓜的描述。匹配到的结果是画着喜羊羊的西瓜。
如上面的例子,/a(?=b)/g ,abacc字符串匹配到的结果只有b前面的a,而c前面的a是不会匹配到的,而不是ab。
(2)后行断言
/(?<=a)b/g 这是一个简单的后行断言,当b的前面的值为a时,匹配b。abccb字符串匹配到的结果是a后边的b,不会匹配c后边的b。
(3)正向否定查找
/a(?!b)/这是一个简单的正向否定查找,当a的后面的值不是b时,匹配a
(4)反向否定查找
/(?<!a)/b 这是一个简单的反向否定查找,当b前面的值不是a是,匹配b
注意:先行断言、后行断言、正向否定查找和反向否定查找都是匹配项的描述,是一个形容词,不是有效的匹配项。
这个描述有时是一个很宽泛的匹配性如/a(?=[b-z])/,则a后面跟着b到z 的任意字符都是可以的,但是如果跟着一个数字就不可以。
例如:先行断言之后又跟着一个数字的匹配项,如:/a(?=[b-z])[0-9]/那么这个正则就是无效的,无论什么字符串都将返回false,因为必须匹配a后边跟着b到z的字符串,a后边又必须是[0-9],这互相冲突了。将他们拆解成两个正则或许更清晰一些:字符串必须同时满足/a(?=[b-z])/和/a[0-9]/,根本不存在这样的字符串。
还有就是使用先行断言时需要注意/^a(?<=\.js)$/,这样是匹配不到任何结果的,因为这样是必须匹配以a结尾,而且a后边还得跟着.js,这样是互相冲突的,正确的写法应该是/^a\.js$/
(完)