正则表达式是一种用来匹配字符串的强有力的武器。
它的设计思想是用一种描述性的语言定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了。
在 JavaScript 中,正则表达式也是对象,构建正则表达式有两种方式:
- 字面量创建,其由包含在斜杠之间的模式组成
const reg = /\d+/g
- 调用 RegExp 对象的构造函数
使用构建函数创建,第一个参数可以是一个变量,遇到特殊字符需要使用
const reg = new RegExp('\\d+', 'g')
const rul = '\\d+'
const reg1 = new RegExp(rul, 'g')\
进行转义
匹配规则
量词 | 描述 |
---|---|
\* | (0, n)次 |
+ | (1, n)次 |
? | (0, 1)次 |
{n, m} | (n, m)次 |
{n} | (n)次 |
元字符 | 描述 |
---|---|
\ | 转义符 |
. | 除换行符和行结束符以外的任意字符 |
\w | 数字、字母、下划线 |
\W | 非数字、字母、下划线 |
\d | 数字 |
\D | 非数字 |
\s | 空白字符 |
\S | 非空白字符 |
\b | 单词边界 |
\B | 非单词边界 |
\0 | NUL 字符 |
\n | 换行符 |
\f | 换页符 |
\r | 回车符 |
\t | 制表符 |
\v | 垂直制表符 |
\xxx | 八进制数 xxxx 规定的字符 |
\xdd | 十六进制数 dd 规定的字符 |
\uxxxx | 十六进制 xxxx 规定的 Unicode 字符 |
| 其它语法 | 描述 |
| -------- | -------------------------- | ------------------- |
| ^ | 以哪一个元字符作为开始 |
| [^] | 不包含 |
| $ | 以哪一个元字符作为结束 |
| x | y | x 或者 y 中一个字符 |
| [xyz] | x 或者 y 或者 z 中一个字符 |
| [^xyz] | 除了 x 或者 y 或者 z |
| [a-z] | a-z 范围 |
| () | 分组符号 |
| (?:)
| 只匹配不捕获 |
| (?=)
| 正向先行断言 |
| (?!)
| 负向先行断言 |
| (?<=)
| 正向后行断言 |
| (?<!)
| 负向后行断言 |
标志 | 描述 |
---|---|
g | 全局搜索 |
i | 不区分大小写搜索 |
m | 多行搜索 |
s | 允许 . 匹配换行符 |
u | 使用 unicode 码的模式进行匹配 |
y | 执行粘性搜索,匹配从目标字符串的当前位置开始 |
贪婪模式
在了解贪婪模式前,首先举个例子:
const reg = /ab{1,3}c/
在匹配过程中,尝试可能的顺序是从多往少的方向去尝试。首先会尝试 bbb,然后再看 整个正则是否能匹配。
不能匹配时,吐出一个 b,即在 bb 的基础上,再继续尝试,以此重复。
如果多个贪婪量词挨着,则深度优先搜索:
const string = '12345'
const reg = /(\d{1,3})(\d{1,3})/
console.log(string.match(reg))
// => ["12345", "123", "45", index: 0, input: "12345"]
其中,前面的 \d{1,3}
匹配的是 123
,后面的 \d{1,3}
匹配的是 45
。
懒惰模式( 非贪婪模式)
重复类量词都具有贪婪性,在条件允许的前提下,会匹配尽可能多的字符。
惰性量词就是在贪婪量词后面加个问号,表示尽可能少的匹配:
var string = '12345'
var regex = /(\d{1,3}?)(\d{1,3})/
console.log(string.match(regex))
// => ["1234", "1", "234", inde×: 0, input: "12345"]
其中 \d{1,3}?
只匹 配到一个字符 1
,而后面的 \d{1,3}
匹配了 234
。
?
、{n}
和{n,m}
重复类具有弱贪婪性,表现为贪婪的有限性。*
、+
和{n,}
重复类具有强贪婪性,表现为贪婪的无限性。{n,m}?
:尽量匹配 n 次,但是为了满足限定条件也可能最多重复 m 次。{n}?
:尽量匹配 n 次。{n,}?
:尽量匹配 n 次,但是为了满足限定条件也可能匹配任意次。??
:尽量匹配,但是为了满足限定条件也可能最多匹配 1 次,相当于{0,1}?
。+?
:尽量匹配 1 次,但是为了满足限定条件也可能匹配任意次,相当于{1,}?
。*?
:尽量不匹配,但是为了满足限定条件也可能匹配任意次,相当于{0,}?
。
分组
分组主要是用过 ()
进行实现,比如 beyond{3}
,是匹配 d 字母 3 次。而 (beyond){3}
是匹配 beyond 3 次。
在 ()
内使用 |
达到或的效果,如 (abc|Xx)
可以匹配 abc 或者 Xx。
反向引用时,巧用 $
分组捕获:
let str "John Smith";
//交换名字和姓氏
console.log(str.replace(/(john) (smith)/i, '$2, $1')) // Smith, John
反向引用(分组符号)
使用小括号可以对字符模式进行任意分组,在小括号内的字符串表示子表达式,也称为子模式。
子表达式具有独立的匹配功能,保存独立的匹配结果;同时,小括号后的量词将会作用于整个子表达式。
通过分组可以在一个完整的字符模式中定义一个或多个子模式。当正则表达式成功地匹配目标字符串后,也可以从目标字符串中抽出与子模式相匹配的子内容。
var s = 'ab=21, bc=45, cd=43'
var r = /(\w+)=(\d*)/g
while ((a = r.exec(s))) {
console.log(a) //返回类似["ab=21","bc=45","cd=43"]三个数组
}
var s = '<h1>title<h1><p>text<p>'
var r = /(<\/?\w+>).*\1/g
var a = s.match(r) //返回数组["<h1>title<h1>","<p>text<p>"]
对子表达式的引用,是指引用前面子表达式所匹配的文本,而不是子表达式的匹配模式。如果要引用前面子表达式的匹配模式,则必须使用下面方式
var s = '<h1>title</h1><p>text</p>'
var r = /((<\/?\w+>).*(<\/?\w+>))/g
var a = s.match(r) //返回数组["<h1>title</h1>","<p>text</p>"]
var s = 'abc'
var r = /(a(b(c)))/
var a = s.match(r) //返回数组["abc","abc","bc","c"]
var s = 'abcdefghijklmn'
var r = /(\w)(\w)(\w)/
r.test(s)
console.log(RegExp.$1) //返回第1个子表达式匹配的字符a
console.log(RegExp.$2) //返回第2个子表达式匹配的字符b
console.log(RegExp.$3) //返回第3个子表达式匹配的字符c
var s = 'aa11bb22c3d4e5f6'
var r = /(\w+?)(\d+)/g
var b = s.replace(r, '$2$1')
console.log(b) //返回字符串"11aa22bb3c4d5e6f"
反向引用会占用一定的系统资源,在较长的正则表达式中,反向引用会降低匹配速度。如果分组仅仅是为了方便操作,可以使用
(?:)
禁止反向引用。
断言
(让我绕的最晕的地方……)直接看例子:
\w+(?=ing)
匹配以 ing 结尾的多个字符(不包括 ing)
\w+(?!ing)
匹配不是以 ing 结尾的多个字符
(?<=re)\w+
匹配以 re 开头的多个字符(不包括 re)
(?<!re)\w+
匹配不是以 re 开头的多个字符
(?<=\s)\d+(?=\s)
匹配两边是空白符的数字,不包括空白符
匹配方法
正则表达式常被用于某些方法,我们可以分成两类:
- 字符串(str)方法:match、matchAll、search、replace、split
- 正则对象下(regexp)的方法:compile、test、exec
RegExp 对象方法 | 描述 |
---|---|
compile | 编译正则表达式 |
exec | 检索字符串中匹配 RegExp 的部分,有匹配返回一个数组,未匹配则返回 null |
test | 检查字符串是否匹配 RegExp,返回 true 或 false |
String 对象方法 | 描述 |
---|---|
search | 返回字符串中匹配到的位置索引,失败时返回 -1 |
match | 检索字符串中匹配 RegExp 的部分,有匹配返回一个数组,未匹配则返回 null |
matchAll | 检索字符串中所有匹配 RegExp 的部分,返回一个迭代器(iterator) |
replace | 替换与正则表达式匹配的子串 |
split | 按照匹配的 RegExp 子串把字符串分割为字符串数组 |