Skip to main content

正则表达式查询

正则表达式是一种用来匹配字符串的强有力的武器。

它的设计思想是用一种描述性的语言定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了。

在 JavaScript 中,正则表达式也是对象,构建正则表达式有两种方式:

  1. 字面量创建,其由包含在斜杠之间的模式组成
const reg = /\d+/g
  1. 调用 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非单词边界
\0NUL 字符
\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

note
  • ?{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 子串把字符串分割为字符串数组