前言
什么是正则表达式?
我们在日常开发中总是能遇到一些需要校验的功能,例如校验邮箱、手机号等等,或者对一些字符串做替换处理等,正则表达式总是避不开的。个人理解正则就是用来匹配字符,校验字符规则,或者是提取一段字符中的内容。本文只介绍正则的一些基本使用。
匹配字符
匹配模糊长度
js
const regexp = /ab{1,5}c/g
const str = "abc abbc abbbc abbbbbbbc"
console.log(str.match(regexp)) // [ 'abc', 'abbc', 'abbbc' ]
通过 {m,n} 的形式来设置匹配的长度范围,以上代码匹配的就是 b 出现 1 - 5 次。而且 {m,n}中,可以省略,代表匹配出现至少 m 次,或者只写 m,{m} 代表匹配只出现 m 次。
匹配模糊字符
js
const regexp = /a[bcde]a/g
const str = "aba aca ada aaa"
console.log(str.match(regexp)) // [ 'aba', 'aca', 'ada' ]
通过 [] 的形式来设置可能出现的字符,以上代码匹配的就是 bcde
中出现任意一个。
排除某些字符用 ^
js
const regexp = /a[^bcde]c/g
const str = "abc acc adc aac"
console.log(str.match(regexp)) // [ 'aac' ]
通过 ^ 来设置不匹配哪些字符,以上代码匹配的就是 除了 bcde
字符外任意字符
多分支匹配
js
const regexp = /a|b|cd|efg/g
const str = "adbdcdefg"
console.log(str.match(regexp)) // [ 'a', 'b', 'cd', 'efg' ]
多分支匹配通过 | 实现,意思是匹配其中任意一个即可,和 JS 中的 或
类似,另外需要注意当前面的分支匹配上后就不会继续进行匹配了。
匹配位置
锚字符
- ^ 匹配开头
- $ 匹配结尾
- \b 单词字符和非单词字符之间,也就是 \w 和 \W 之间
- \B 把 \b 取反
- (?=p) p 前的位置
- (?=p) 除了p 前的位置
开头结尾 ^ $
js
const regexp = /^ab$/g
var string = "ab";
console.log(string.match(regexp)) // [ 'ab' ]
以上代码就是匹配以 a 开头,b 结尾的字符串,另外字符串有一个方法 replace 可以通过匹配位置进行替换。
js
var string = "abdefg";
console.log(string.replace(/^|$/g, '#')) // #abdefg#
通过匹配字符串的开头和结尾位置,替换成 #。
js
var string = "a\nb\nd\ne\nf\ng";
console.log(string.replace(/^|$/gm, '#'))
/*
#a#
#b#
#d#
#e#
#f#
#g#
*/
需要注意的是,在多行模式匹配下,开头和结尾是每行的开头结尾。
单词边界 \b \B
js
var string = "Hello,[JS] Hello World";
console.log(string.replace(/\b/g, '#')) // #Hello#,[#JS#] #Hello# #World#
单词边界就是 \w 和 \W 之间,也就是所有数字、字符串以及下划线 和 其他字符之间,也就是下图箭头所指的位置。注意看箭头两边的字符分别是\w 和 \W,另外要注意的是开头和结尾也是 \W。
那 \B 就很好理解了,就是除了 \w 所指位置的其他位置。
先行断言
(?=p)
(正向先行断言)指 p 前面的位置,这个 p 就是一个子模式,先用 p 进行匹配,然后再取他前面的位置
js
const string = "Hello,[JS] Hello World";
console.log(string.replace(/(?=\w)/g, '#')) // #H#e#l#l#o,[#J#S] #H#e#l#l#o #W#o#r#l#d
(?!p)
(负向先行断言)就是和 (?=p)
相反,就是先匹配非 p 然后取前面的位置
js
console.log(string.replace(/(?!\w)/g, '#')) // Hello#,#[JS#]# Hello# World#
(?<=p)
指 p 后面的位置
js
console.log(string.replace(/(?<=\w)/g, '#')) // H#e#l#l#o#,[J#S#] H#e#l#l#o# W#o#r#l#d#
(?>!p)
和 (?<=p)
相反
js
console.log(string.replace(/(?<!\w)/g, '#')) // #Hello,#[#JS]# #Hello #World
实践
给金额加上千分位逗号 例如 123456789 -> 123,456,789
解题思路:先选中倒数第三位前的位置替换成逗号,然后出现多次,最后排除开头的位置。
我们要做的就是选中图中箭头的位置
第一步
js
const string = "123456789";
const regexp = /(?=\d{3}$)/g
console.log(string.replace(regexp, ",")); // 123456,789
上面的正则匹配的就是以三个数字结尾前的位置,然后替换成逗号。
第二步
js
const regexp = /(?=(\d{3})+$)/g
console.log(string.replace(regexp, ",")); // ,123,456,789
通过 +
让三个数字匹配多次,达到匹配上每三位数字前的位置,然后替换成逗号,这时发现开头的位置多了一个逗号,我们需要排除掉开始的位置
第三步
js
const regexp = /(?!^)(?=(\d{3})+$)/g
console.log(string.replace(regexp, ",")); // 123,456,789
通过(?!^)
匹配除了开始位置的其他位置,这种方法就排除掉了开头,达到了预期的效果。
影响量词的匹配行为
贪婪匹配
正则在进行量词匹配时,会尽可能地多匹配。
js
const regexp = /a{1,3}/g
const str = "aaa aaaa aaaaa"
console.log(str.match(regexp)) // [ 'aaa', 'aaa', 'a', 'aaa', 'aa' ]
惰性匹配(非贪婪匹配)
我们使用 ? 来声明惰性匹配,惰性匹配每次只满足最低要求即可。
js
const regexp = /a{1,3}?/g
const str = "aaa"
console.log(str.match(regexp)) // [ 'a', 'a', 'a' ]
分组
我们可以通过()
来对正则表达式进行分组,如
js
const regexp = /(abc)+/g
const str = "abcabc abccc"
console.log(str.match(regexp)) // [ 'abcabc', 'abc' ]
把 abc
括起来使他们成为一个分组,+
的就是这个分组。
提取分组的数据
我们对正则进行分组匹配后还可以拿到分组匹配到的数据,
js
const regexp = /(\d+)([a-z]+)/
const str = "1231abc"
console.log(str.match(regexp))
console.log(regexp.exec(str))
两个输出结果相同,输出结果:
需要注意的是,在捕获分组的时候,使用全部匹配 /g
就不返回分组信息了。
js
const regexp = /(\d+)([a-z]+)/g
const str = "1231abc"
console.log(str.match(regexp)) // [ '1231abc' ]
反向引用
在正则表达式中,如果有一个不确定的字符,后面还需要用到这个相同的字符,就可以用反向引用的方式
js
const regexp = /\d{3}([,\-])\d{3}\1\d{3}/
const str = "123,456,789";
const str2 = "123-456-789";
const str3 = "123-456,789";
console.log(regexp.test(str)) // true
console.log(regexp.test(str2)) // true
console.log(regexp.test(str3)) // false
这里我们用 \1
来引用第一个分组的字符,所以当前面是 ,
后面也只能是 ,
,这样就能做到前后一致。
另外如果出现嵌套括号的情况,我们以左括号出现的顺序为引用的顺序
js
const regexp = /^((\d)(\d(\d)))\1\2\3\4$/;
/*
* \1 => ((\d)(\d(\d)))
* \2 => (\d)
* \3 => (\d(\d))
* \4 => (\d)
* */
如果不希望分组被提取引用,使用非捕获分组 (?:p)
,我们把上面的代码修改一下看结果。
js
const regexp = /(\d+)([a-z]+)/
const str = "1231abc"
console.log(str.match(regexp))
输出结果:
附录
详细查阅: 正则表达式
匹配数量简写
- {m,} 至少出现 m 次数
- {m} 只出现 m 次数
- ? 不出现或者出现一次 等价于 {0,1}
-
- 至少出现一次
- * 有没有都行
匹配范围简写
- \d 表示数字
- \D 除了数字外任意字符
- \w 数字+字母+下划线
- \W 除了 \w
- \s 空白字符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符
- \S 非空白符
- . 任意字符
修饰符
- i 不区分大小写
- g 全局匹配
- m 多行匹配
- s 允许 . 匹配换行符。
- u 使用 unicode 码的模式进行匹配。
- y 粘性 匹配从目标字符串的当前位置开始。