正则表达式详解
正则表达式是一个非常有用的用来匹配验证字符串的工具。很多人觉得正则表达式规则繁多,学了也会忘记,不如等到需要使用的时候再去查阅即可。这样的观点也无可厚非,不过我曾经看到过一个绝妙的关于阅读的比喻,阅读就像是将竹篓浸入到水里面,虽然捞打上来的时候竹篓里并没有装水,但是竹篓已经被浸湿了。学习正则表达式也是如此,循序渐进,方能厚积薄发。
大纲
◆ 常用的正则表达式
◆ 分组捕获与后向引用
◆ 贪婪模式与非贪婪模式
◆ 应用场景
常用的正则表达式
1.元字符
元字符 | 释义 |
---|---|
普通字符 | 如A-Z、a-z、0-9 等等 |
. | 除换行以外的其他任意字符, 如果需要匹配换行符,可用[.n] |
s | 空白字符 |
S | 除空白字符以外的任意字符 |
w | 字母、数字、下划线 |
W | 除了字母、数字、下划线以外的任意字符 |
d | 数字 0-9 |
D | 除了数字之外的任意字符 |
^ | 匹配输入字符串开始的位置 |
$ | 匹配输入字符串结尾的位置 |
b | 单词边界,匹配一个完整的单词可以使用 bwordb |
以上是最常用的正则表达式匹配字符,当然,还有很多其他的匹配元字符,比如\t
匹配制表符,\r
匹配回车符,\n
匹配换行符等。
以\d
为例,\d
代表数字0-9,等价于字符组[0-9]
例:匹配字符串 ‘fefafe332gt66ooj44nie85kk’ 中的所有数字
var str = 'fefafe332gt66ooj44nie85kk' var reg = /\d+/g console.log(str.match(reg)) // 结果为["332", "66", "44", "85"]
2.限定符
限定符 | 释义 |
---|---|
* | 0到无数次。例如,zo* 能匹配 "z" 以及 "zoo" |
+ | 1到无数次。例如,zo+ 不能匹配 “z” 但是能匹配 “zo”和“zoo” |
? | 0 或者1 次。例如,zo? 能匹配 “z”和 “zo” 但是不能匹配 “zoo” |
{n} | 匹配确定的N次。 例如, zo{2} 只能匹配到 zoo |
{n,} | 匹配n到无限次。 例如,zo{1,} 不能匹配“z” 但是能匹配“zo”和“zoo” |
{n,m} | 匹配n到m次。注意:在逗号和两个数之间不能有空格 |
以上这些限定符,可以匹配指定个数的字符,在能够匹配的范围之内,尽可能多的匹配。其中{n,m}
限定符有两个注意点:
- n不能大于m
- 在逗号和两个数之间不能有空格,否则无法匹配
例:匹配38到288之间的数
let reg = /\b((38|39)|([4-9]\d)|(1\d{2})|(2[0-7]\d)|(28[0-8]))\b/g let str = '45 454 255 288 38 88 11 37 100 109 28 000 289 209' console.log(str.match(reg)) // ["45", "255", "288", "38", "88", "100", "109", "209"]
3.修饰符
修饰符 | 释义 |
---|---|
g | global 全局搜索 (不添加 搜索到第一个匹配停止) |
i | ignore case 忽略大小写 |
m | multiline 多行匹配 |
m多行匹配用得相对较少,但是也有一定用处。
例:对比m和mg
var str8 = "abeifenabc\nabpheeabc" var reg8a = /^ab/gm var reg8b = /^ab/g console.log(str8.match('gm:'+reg8a)) // 结果为:["ab", "ab"] console.log(str8.match('g:'+reg8b)) // 结果为:["ab"]
上面三个修饰符最常用。当然,还有其他修饰符,比如A
表示强制从目标字符串开头匹配,x
表示将模式中的空白忽略。
4.其他
常用符号 | 释义 | |
---|---|---|
分支符号"\ | " | 用来匹配指定几个规则中的一个 |
转义符号"\" | 用于匹配"[""^""+"")"等有特殊含义符号 | |
字符组 [] | 用于匹配指定范围之内的任意一个字符 |
例如:表达式 [cChH]at
可以只能匹配到 cat
、cat
、hat
、hat
四个字符串中的一个
例:敏感词过滤。比如:“我草你妈哈哈背景天胡景涛哪肉涯剪短发欲望”,过滤:'草肉欲胡景涛'
let str9 = '我草你妈哈哈背景天胡景涛哪肉涯剪短发欲望'; let regExp = /草|肉|欲|胡景涛/g; let result = str9.replace(regExp, function(match) { let len = match.length; let str; switch (len) { case 1: str = '*'; break; case 2: str = "**"; break; case 3: str = "***"; break; default: str = '****'; } return str; }); console.log(result); //我*你妈哈哈背景天***哪*涯剪短发*望
分组捕获与后向引用
1.分组捕获
当我们想要匹配多个字符的时候,可以使用限定符来指定个数,那当我们需要匹配多个字符串的情况,该怎么办呢?可以使用分组捕获。
- 概念:
分组捕获是指将想要匹配的正则表达式用小括号括起来,然后与限定符组合使用,可以连续匹配符合规则的字符串。每一个小括号代表的表达式分为一组,作为子表达式,后期可以通过捕获不同组的内容来进行替换等操作。
例:匹配字符串isuwang连续出现3次的情况
isuwang{3}
(isuwang){3}
可以看到,下面一组例子将isuwang字符串当作一个组,整体匹配了3次。
2.非捕获分组
- 分组捕获的缺点:
有些不得不用()但是后期又不会用到的子表达式内容,记录在组里面会占用内存,降低匹配效率
- 解决:
使用非捕获组。只进行分组,并不将子表达式匹配到的内容捕获到组里。
字符 | 描述 | 示例 | |||
---|---|---|---|---|---|
(?:pattern) | 匹配pattern,但不捕获匹配结果。 | ‘industr(?:y\ | ies)匹配'industry'或'industries' | ||
(?=pattern) | 零宽度正向预查,不捕获匹配结果。 | 'Windows (?=95\ | 98\ | NT\ | 2000)'匹配 "Windows2000" 中的 "Windows",不匹配 "Windows3.1" 中的 "Windows"。 |
(?!pattern) | 零宽度负向预查,不捕获匹配结果。 | 'Windows (?!95\ | 98\ | NT\ | 2000)'匹配 "Windows3.1" 中的 "Windows"不匹配 "Windows2000" 中的 "Windows"。 |
(?<=pattern) | 零宽度正向回查,不捕获匹配结果。 | '2000 (?<=Office\ | Word\ | Excel)'匹配 " Office2000" 中的 "2000",不匹配 "Windows2000" 中的 "2000"。 | |
(?<!pattern) | 零宽度负向回查,不捕获匹配结果。 | '2000 (?<!Office\ | Word\ | Excel)'匹配 " Windows2000" 中的 "2000",不匹配 " Office2000" 中的 "2000"。 |
- 使用情况:
一般只有在比较复杂,“()”使用较多的情况下会考虑使用非捕获组。比如验证日期的正则表达式。
- 对比:
未使用非捕获组的正则
^((?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)|([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)$
使用了非捕获组的正则:
^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$
3.后向引用
- 作用:
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1
代表分组1匹配的文本,也可以用$1
来表示
- 说明:
子分组捕获的内容可在正则表达式里面或者其他文本里面作进一步的处理。默认情况下,从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。也可以使用命名分组的方式,对每个分组进行命名。
例:检查重复字符串\
给定字符串 str,检查其是否包含连续重复的字母(a-zA-Z),包含返回 true,否则返回false
let str4 = 'abccdefgjhiss' let reg4a = /([a-zA-Z])\1/g let reg4b = /([a-zA-Z]){2}/g console.log(str4.match(reg4a)) // ["cc", "ss"] console.log(str4.match(reg4b)) // ["ab", "cc", "de", "fg", "jh", "is"]
4.贪婪模式和非贪婪模式
- 贪婪模式:
在整个表达式匹配成功的前提下,尽可能多的匹配
- 非贪婪模式:
整个表达式匹配成功的前提下,尽可能少的匹配。非贪婪模式只被部分NFA引擎所支持。
- 量词:
贪婪模式 | 非贪婪模式 |
---|---|
{m,n} | {m,n}? |
{m,} | {m,}? |
? | ?? |
* | *? |
+ | +? |
例子:
提取两个""中的子串,其中不能再包含""\
字符串:"The colleagues in "kuaisuwang" are very "diligent" and united"
错误解法(通过非贪婪匹配)
let str6='"The colleagues in "isuwang" are very "diligent" and united"' let reg6a=/".*?" /g // 注意"后面还有个空格 console.log(str6.match(reg6a)) // [""The colleagues in "isuwang" ", ""diligent" "]
正确解法(通过贪婪模式匹配)
let str6='"The colleagues in "isuwang" are very "diligent" and united"' let reg6b=/"[^"]*" /g // 注意"后面还有个空格 console.log(str6.match(reg6b)) //[""isuwang" ", ""diligent" "]
总结
此篇文章主要讲述了常用的正则表达式符号,以及分组和后向引用,贪婪模式和非贪婪模式两种模式,这些知识点呢,在工作当中会非常有用。至于正则表达式在我工作中的运用,我放在下一篇来跟大家进行分享。