发布于

正则表达式

Authors
  • avatar
    Name
    田中原
    Twitter

一、创建

1、字面量方式

var regExp = /[bc]at/i

2、构造函数方式

var regExp = new RegExp('[bc]at', 'i')
(1)构造函数属性
  • a、input($_):最近一次要匹配的字符串

  • b、lastMatch($&):最近一次的匹配项

  • c、lastParen($+):最近一次匹配的捕获组

  • d、leftContext($`):input 字符串中 lastMatch 之前的文本

  • e、rightContext($'):input 字符串中 lastMatch 之后的文本

  • f、multiline($*):Boolean 类型值,表示是否所有表达式都使用多行模式。

Opera 未实现 a、b、c 属性,IE、Opera、Chrome 未实现 f 属性

var text = 'this has been a ,short summer'
var reg = /(.)hort/g
if (reg.test(text)) {
  console.log(RegExp.input) //this has been a ,short summer
  console.log(RegExp.$_) //this has been a ,short summer
  console.log(RegExp.leftContext) //this has been a ,
  console.log(RegExp.rightContext) // summer 前面有空格
  console.log(RegExp.lastMatch) //short
  console.log(RegExp.lastParen) //s
  console.log(RegExp.length) //2
  console.log(RegExp.multiline) //undefined
}

3、区别

  • 传递给 RegExp 构造函数的两个参数都是字符串

  • 在 ECMAScript 3 中,正则表达式子面量始终会共享同一个 RegExp 实例,而使用构造函数创建的每一个新 RegExp 实例都是一个新实例。

  • 在 ECMAScript 5 中已明确规定,使用正则表达式字面量必须像直接调用 RegExp 构造函数一样,每次都创建新的 RegExp 实例。

  • 字面量创建方式元字符不需要转义,实例创建方式需要转义。

    var reg1 = new RegExp('d') //    /d/
    var reg2 = new RegExp('\\d') //   /\d/
    var reg3 = /\d/ //  /\d/
    

字符 \ 在字符串中通常被转义为 \\,而在正则表达式字符串中就会变成 \\\\ 。

var str = '\\'
str.match(/\\/g) // ["\"]

var str = '\\\\'
str.match(/\\/g) // ["\", "\"]

//正则表达字面量
var re = /\\/g

//正则构造函数
var reg = new RegExp('\\\\', 'g')
var foo = 'abc\\123' //foo的值为"abc\123"
console.log(re.test(foo)) //true
console.log(reg.test(foo)) //true

二、实例属性

  • global: 布尔值,是否设置了 g 标志

  • ignoreCase: 布尔值,是否设置了 i 标志

  • lastIndex: 整数,表示开始搜索下一个匹配的字符位置,从 0 算起

  • multiline: 布尔值,是否设置了 m 标志

  • source: 正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回

// 1、字面量

var re = /[bc]at/gi // 修饰符顺序随意

re.global // true
re.ignoreCase // true
re.multiline // false
re.source // "[bc]at"
re.lastIndex // 0

// 2、构造函数
var re1 = new RegExp('[bc]at', 'i')
re1.global // true
re1.ignoreCase // false

var re2 = new RegExp('[bc]at', 'ig')
re2.global // true
re2.ignoreCase // true
re2.multiline // false
re2.source // "[bc]at"
re2.lastIndex // 0

var text = 'mom and dad and baby'
var re3 = /(an)/gi
var matches = re3.exec(text)
cosnole.log(matches) // ["an", "an", index: 4, input: "mom and dad and baby", groups: undefined] // length: 2

re3.lastIndex // 6 找到第一个匹配项位置为 4 (指的是 a)然后下一个位置从 6 开始的。

三、实例方法

1、test()

test() 返回 Boolean,查找对应的字符串中是否存在模式。
var str = '452-2344-42'
var str2 = '42-2344-42'
var str3 = 's42-2344-42'
var pattern = /^\d{3}-\d{4}-\d{2}$/

// 判断字符串是否满足 XXX-XXXX-XX 的格式,X为数字
function my(str) {
  return pattern.test(str)
}

console.log(my(str)) //true
console.log(my(str2)) //false
console.log(my(str3)) //false

2、exec() [eɡ'zek]

专门为 捕获组【 () 】而设计的,此方法接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;在没有匹配项的情况下返回 null。返回的数组实例包含两个额外的属性: index 和 input 。其中 index 表示匹配项在字符串中的位置。 input 表示应用正则表达式的字符串。

var str = 'car,bat,sat,asr'
var pattern = /.(a)t/

var matches = pattern.exec(str)
console.log(matches) //["bat", index: 4, input: "car,bat,sat,asr"]
console.log(matches[0], matches[1]) //bat  a
console.log(matches.index) //4

matches = pattern.exec(str)
console.log(matches) //["bat", index: 4, input: "car,bat,sat,asr"]
console.log(matches[0], matches[1]) //bat a
console.log(matches.index) //4

// 括号中的分组捕获
var re = /quick\s(brown).+?(jumps)/gi
var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog')
console.log(result[1], result[2]) // Brown Jumps

var pattern2 = /.at/g
var matches = pattern2.exec(str)
console.log(matches) //["bat", index: 4, input: "car,bat,sat,asr"]
console.log(matches[0], matches[1]) //bat a
console.log(matches.index) //4

matches = pattern2.exec(str)
console.log(matches) //["bat", index: 4, input: "car,bat,sat,asr"]
console.log(matches[0], matches[1]) //sat a
console.log(matches.index) //8

console.log(/ss/.exec(str)) //null

3、exec() VS match()

  1. 当正则表达式无子表达式,并且定义为非全局匹配时,exec和match执行的结果是一样,均返回包含第一个匹配项信息的数组;1⃣️

  2. 当正则表达式无子表达式,并且定义为全局匹配时,exec和match执行,做存在多处匹配内容,则exec 返回包含第一个匹配项信息的数组, match返回的是多个元素数组;2⃣️

  3. 当正则表达式有捕获组【子表达式】时,并且定义为非全局匹配,exec和match执行的结果是一样;3⃣️

  4. 当正则表达式有捕获组【子表达式】时,并且定义为全局匹配,exec和match执行的结果不一样,此时match将忽略子表达式,只查找全匹配正则表达式并返回所有内容;4⃣️

子表达式:子表达式是一个更大的表达式的一部分,把一个表达式划分为一系列表达式的目的是为了把那些表达式当作一个独立元素来使用。子表达式必须用(和)括起来。注意:(和)是元字符,如果需要匹配(和)本身,就必须使用它的转义序列\(和\) 。子表达式是可以提高可读性,对表达式的实际行为也没有任何不良影响,但对匹配操作的速度可能会有点儿影响。

捕获组:捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用。当然,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部。

// 1⃣️
var reg = /\wag/
var str = 'a bagwith a tag has a mag'
reg.exec(str) // ["bag", index: 2, input: "a bagwith a tag has a mag", groups: undefined]

'a bagwith a tag has a mag'.match(/\wag/) // ["bag", index: 2, input: "a bagwith a tag has a mag", groups: undefined]

// 2⃣️
var reg = /\wag/g
var str = 'a bagwith a tag has a mag'
reg.exec(str) // ["bag", index: 2, input: "a bagwith a tag has a mag", groups: undefined]
'a bagwith a tag has a mag'.match(/\wag/g) //  ["bag", "tag", "mag"]

// 3⃣️
var reg = new RegExp('a(bc)')
var str = '3abc4,5abc6'
console.log(reg.exec(str)) // ["abc", "bc", index: 1, input: "3abc4,5abc6", groups: undefined]
console.log(reg.exec(str)[1], reg.exec(str)[2]) // bc undefined

console.log(str.match(reg)) // ["abc", "bc", index: 1, input: "3abc4,5abc6", groups: undefined]
console.log(str.match(reg)[1], str.match(reg)[2]) // bc undefined

// 4⃣️

var reg = new RegExp('a(bc)', 'g')
var str = '3abc4,5abc6'
console.log(reg.exec(str)) // ["abc", "bc", index: 1, input: "3abc4,5abc6", groups: undefined]

console.log(str.match(reg)) // ["abc", "abc"]

var reg = new RegExp('a(bc)')
var str = '3abc4,5abc6'
console.log(reg.exec(str)) //  ["abc", "bc", index: 1, input: "3abc4,5abc6", groups: undefined]

console.log(str.match(reg)) // ["abc", "bc", index: 1, input: "3abc4,5abc6", groups: undefined]

4、字符串的正则方法

  • match()(属于 String 正则表达方法)

语法:str.match(regexp)

str:要进行匹配的字符串;regexp:一个正则表达式(或者由RegExp()构造成的正则表达式)

match的用法主要区分正则表达式是否有全局标示g。

(1)如果没有g全局标志,那么返回的数组arr。arr[0]保存的是第一个匹配的字符串内容。1⃣️

(2)如果有g全局标志,那么返回的数组保存的所有匹配的内容。2⃣️

(3)如果有捕获组,无g全局标志,那么返回的数组arr。arr[0] 保存的是完整的匹配,arr[1]保存的是第一个括号里捕获的字串,依此类推arr[n]保存的是第n个括号捕获的内容。3⃣️

(4)如果有捕获组,有g全局标志,那么返回的数组arr。那么返回的数组保存的所有匹配的内容。4⃣️

var str = 'car,bat,sat,asr'

// 1⃣️
var ns = str.match(/.at/)
console.log(ns) //["bat", index: 4, input: "car,bat,sat,asr"]
console.log(ns.index) //4
console.log(ns[0], ns[1]) //bat undefined

// 2⃣️
var ns2 = str.match(/.at/g)
console.log(ns2) //["bat", "sat"]
console.log(ns2.index) //undefined
console.log(ns2[0], ns2[1]) //bat sat

// 3⃣️
var reg = /(.)(at)/g
console.log(str.match(reg)) // ["bat", "sat"]

// 4⃣️
var matchStr = str.match(/(.a)(t)/)
console.log(matchStr) // ["bat", "ba", "t", index: 4, input: "car,bat,sat,asr", groups: undefined]

// 5⃣️
console.log(str.match(/ass/)) //null
  • search():返回索引, 没匹配到返回 -1
var str = 'car,bat,sat,asr'

// 1、search()返回索引, 没匹配到返回 -1
var myindex = str.search(/.at/)
console.log(myindex) //4

// 2、
console.log(str.search(/ase/)) //-1

// 3、
var myindex2 = str.search(/.at/g)
console.log(myindex2) //4
  • replace() : (原字符串不改变)返回处理后的字符串,没匹配的返回原字符串
var str = 'car,bat,sat,asr'

// 1、replace() , 源字符串不改变,返回处理后的字符串
console.log(str.replace('at', 'one')) //car,bone,sat,asr
console.log(str) //car,bat,sat,asr

// 2、
console.log(str.replace(/.at/, 'one')) //car,one,sat,asr // 如果没有全局修饰符g,则只匹配第一个并进行替换。
console.log(str.replace(/.at/g, 'one')) //car,one,one,asr // 有有全局修饰符g,则全部匹配并进行替换。

// 3、
console.log(str.replace(/.at/, 'one(1)')) //car,one(1),sat,asr
console.log(str.replace(/(.at)/, 'one($1)')) //car,one(bat),sat,asr // 正则外捕获组
console.log(str.replace(/(.at)/g, 'one($1)')) //car,one(bat),one(sat),asr // 正则外捕获组

// 4、item为匹配字符的值,index是item的索引,originalText为原始字符串
str.replace(/[a-z]/g, (item, index, originalText) => {
  console.log(item, index, originalText)
})
// 打印内容为:
c 0 car,bat,sat,asr
a 1 car,bat,sat,asr
r 2 car,bat,sat,asr
b 4 car,bat,sat,asr
a 5 car,bat,sat,asr
t 6 car,bat,sat,asr
s 8 car,bat,sat,asr
a 9 car,bat,sat,asr
t 10 car,bat,sat,asr
a 12 car,bat,sat,asr
s 13 car,bat,sat,asr
r 14 car,bat,sat,asr
undefinedundefinedundefined,undefinedundefinedundefined,undefinedundefinedundefined,undefinedundefinedundefined
var str = 'car,bat,sat,asr'
var newStr = str.replace(/[a-z]/g, (item, index, originalText) => {
  console.log(item, index, originalText)
  return (item += '+++')
})
console.log(newStr)
// 打印内容为:
c 0 car,bat,sat,asr
a 1 car,bat,sat,asr
r 2 car,bat,sat,asr
b 4 car,bat,sat,asr
a 5 car,bat,sat,asr
t 6 car,bat,sat,asr
s 8 car,bat,sat,asr
a 9 car,bat,sat,asr
t 10 car,bat,sat,asr
a 12 car,bat,sat,asr
s 13 car,bat,sat,asr
r 14 car,bat,sat,asr
c+++a+++r+++,b+++a+++t+++,s+++a+++t+++,a+++s+++r+++
  • split():返回分割后的数组,不改变原字符串。
var a = 'dadasx'

// 1、
console.log(a.split('')) //["d", "a", "d", "a", "s", "x"]
console.log(a.split('', 2)) //["d", "a"]
console.log(a.split('a')) //["d", "d", "sx"]
console.log(a) //dadasx

// 2、将字符串a的顺序颠倒:
console.log(a.split('').reverse().join('')) //xsadad
console.log(a) //dadasx

// 3、用 正则 分割
var a = 'adx#45xlz&45' //遇到#或&就分割
console.log(a.split(/#|&/)) //["adx", "45xlz", "45"]

四、普通字符

五、元字符

指那些在正则表达式中具有特殊意义的专用字符,如果使用元字符的本意,则需要用 “转义字符” 转义。一个元字符只匹配一个字符。即便强大如 . 它也只能匹配一个。

  • \ 转义字符 把元字符转成普通字符的字符。(\\ 转义有问题)

  • ^(开头) ^ 是正则主体的第一个符号,而紧跟着它的字符则是被匹配的开始字符。(eg: 'shttps'.match(/^[a-z]/))

  • (结尾标识符)(结尾标识符) ` 必须是正则主体的最后一个符号,而紧邻它的前一个字符则是被匹配的结尾字符。`

  • \b、\B 所谓单词边界:匹配的也是一个位置,而不是一个字符。单词和空格之间的位置。单词边界,对中文等其他语言是无效的。

  • \d、\D 匹配一个数字

  • \s、\S 空白字符是空格\f\n\r\t\v的总和,其中\f是换页符,\n是换行符,\r是回车符,\t是水平制表符,\v是垂直制表符。

  • \w、\W \w匹配一个字母或者一个数字或者一个下划线。不过要注意,字母指的是26个英文字母,其他的不行。'正则'.match(/\w/); // null

六、量词

  • ?(重复 0 次或者 1 次)

  • +(重复 1 次或者多次,也就是至少 1 次)

  • * (重复0 次或者多次,也就是任意次数)

  • {n}(重复n次)

  • {n,}(重复至少 n 次或更多次)

  • {n,m}(重复 n 到 m 次,包含n次和m次。{n,m}之间不能有空格,空格在正则中是有含义的。)

总结:量词不能紧跟在另一个量词后面,除了 ? 符号。

七、贪婪模式与非贪婪模式

前面提到量词不能紧跟在另一个量词后面。

'https'.match(/http(s)?/) // ["https", "s", index: 0, input: "https", groups: undefined]
'https'.match(/http(s)??/) // ["http", undefined, index: 0, input: "https", groups: undefined]

紧跟在?后面的?它不是一个量词,而是一个模式切换符,从贪婪模式切换到非贪婪模式。

贪婪模式在正则中是默认的模式,就是在既定规则之下匹配尽可能多的文本。因为正则中有量词,它的重复次数可能是一个区间,这就有了取舍。

紧跟在量词之后加上 ? 就可以开启非贪婪模式。怎么省事怎么来。这里的要点是,? 必须紧跟着量词,否则的话它自己就变成量词了。

八、字符组( [a-z]、[0-9]、[0-z] )

只有两种字符是可以用连字符的:英文字母和数字

  1. 字符组中的字符集合只是所有的可选项,最终它只能匹配一个字符。

  2. 字符组是一个独立的世界,元字符不需要转义。

  3. 有两个字符(^ - )在字符组中有特殊含义。

  • a、^ 在字符组中表示取反,不再是文本开始的位置了。如果我就要 ^ 呢?转义。
  • b、- 本来是一个普通字符,在字符组中摇身一变成为连字符。
  1. [0-z] 英文字母可以和数字连起来,数字在前,英文字母的顺序在后面。这和扑克牌1 2 3 4 5 6 7 8 9 10 J Q K是一个道理。
**'abc-3'.match(/[0-z]/); // 只能是[0-z]这样书写,如果[z-0]则会报错。先查找字母,其次查找数字。**

九、捕获组与非捕获组

捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用。当然,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部。

捕获组有两种形式,一种是普通捕获组,另一种是命名捕获组,通常所说的捕获组指的是普通捕获组。语法如下:

  • 普通捕获组:(Expression)

  • 命名捕获组:(?Expression)

1、正则内捕获

正则内捕获使用\数字(eg: \1)的形式,分别对应前面的圆括号捕获的内容。这种捕获的引用也叫反向引用。
在正则表达式内使用 \1 ,代表的是获取正则表达式内的第一个 () 中内容。
'<App>hello regex</App>'.match(/<([a-zA-Z]+)>.*<\/\1>/)

另外还有一个字符串实例方法也支持正则捕获的引用,它就是replace方法。

// 支持正则外捕获的引用。
stringObject.replace(regexp / substr, replacement)

// 一个新的字符串,是用 replacement 替换了 regexp 的第一次匹配或所有匹配之后得到的。
'hello regex'.replace(/\*{2}(.*)*{2}/, '$1')

2、正则外捕获

RegExp就是构造正则的构造函数。如果有捕获组,它的实例属性 $数字 (eg: $1)会显示对应的引用。$1 则捕获的是第一个 () 的内容,依次类推,$2 捕获第二个 () 的内容,……

'@abc'.match(/@(abc)/)
RegExp.$1

'@xyz'.match(/@(xyz)/)
RegExp.$1
// "xyz"

// 如果有两个类似上面的正则匹配,则RegExp构造函数的引用只显示最后一个正则的捕获。

3、捕获命名

使用 \数字 引用捕获必须保证捕获组的顺序不变。现在开发者可以给捕获组命名了,有了名字以后,引用起来更加确定。在捕获组内部最前面加上 ?<key> ,它就被命名了。使用 \k<key> 语法就可以引用已经命名的捕获组。

'<App>hello regex</App>'.match(/<(?<tag>[a-zA-Z]+)>.*<\/\k<tag>>/)

4、非捕获组

通常情况下,开发者只是想在正则中将某些字符当成一个整体看待。捕获组很棒,但是它做了额外的事情,肯定需要额外的内存占用和计算资源。于是正则又有了非捕获组的概念。只要在圆括号内最前面加上 ?: 标识,就是告诉正则引擎:我只要这个整体,不需要它的引用。

// 1-1、
'@abc'.match(/@(abc)/) // ["@abc", "abc", index: 0, input: "@abc", groups: undefined]

// 1-2、
'@abc'.match(/@(?:abc)/) // ["@abc", index: 0, input: "@abc", groups: undefined]

// 2-1、
'@abc'.match(/@(abc)/) // RegExp.$1; // "abc"

// 2-2、
'@abc'.match(/@(?:abc)/) // RegExp.$1; // ""

十、分支 | 代表 或者的意思

'高圆圆'.match(/陈乔恩|高圆圆/) // ["高圆圆", index: 0, input: "高圆圆", groups: undefined]

|就代表或者。字符组其实也是一个多选结构,但是它们俩有本质区别。字符组最终只能匹配一个字符,而分支匹配的是左边所有的字符或者右边所有的字符。

'我喜欢高圆圆'.match(/我喜欢陈乔恩|高圆圆/) // ["高圆圆", index: 3, input: "我喜欢高圆圆", groups: undefined]

因为|是将左右两边一切两半,然后匹配左边或者右边。所以上面的正则显然达不到我们想要的效果。这个时候就需要一个东西来缩小分支的范围。诶,你可能已经想到了:

'我喜欢高圆圆'.match(/我喜欢(?:陈乔恩|高圆圆)/) // ["我喜欢高圆圆", index: 0, input: "我喜欢高圆圆", groups: undefined]

匹配 'x' 但是不记住匹配项。这种叫作非捕获括号,使得你能够定义为与正则表达式运算符一起使用的子表达式。来看示例表达式 /(?:foo){1,2}/。如果表达式是 /foo{1,2}/,{1,2}将只对 ‘foo’ 的最后一个字符 ’o‘ 生效。如果使用非捕获括号,则{1,2}会匹配整个 ‘foo’ 单词。

'我喜欢陈乔恩,他也喜欢高圆圆'.match(/喜欢(陈乔恩|高圆圆)/) // ["喜欢陈乔恩", "陈乔恩", index: 1, input: "我喜欢陈乔恩,他也喜欢高圆圆", groups: undefined]
'我喜欢陈乔恩,他也喜欢高圆圆'.match(/喜欢(陈乔恩|高圆圆)/g) // ["喜欢陈乔恩", "喜欢高圆圆"]
'我喜欢陈乔恩,他也喜欢高圆圆'.match(/喜欢(?:陈乔恩|高圆圆)/g) // ["喜欢陈乔恩", "喜欢高圆圆"]
'我喜欢陈乔恩,他也喜欢高圆圆'.match(/喜欢(?:陈乔恩|高圆圆)/) // ["喜欢陈乔恩", index: 1, input: "我喜欢陈乔恩,他也喜欢高圆圆", groups: undefined]

字符组 与 分支 的区别

字符组最终只能匹配一个字符,而分支匹配的是左边所有的字符或者右边所有的字符。

十、零宽断言

正则中有一些元字符,它不匹配字符,而是匹配一个位置。 比如之前提到的^和$。^的意思是说这个位置应该是文本开始的位置。正则还有一些比较高级的匹配位置的语法,它匹配的是:在这个位置之前或之后应该有什么内容。

零宽(zero-width)是什么意思?指的就是它匹配一个位置,本身没有宽度。
断言(assertion)是什么意思?指的是一种判断,断言之前或之后应该有什么或应该没有什么。

1、零宽肯定先行断言。语法:圆括号内最左边加上 ?=

保留前面,后面为条件。

'CoffeeScript JavaScript javascript'.match(/\b\w{4}(?=Script\b)/) // ["Java", index: 13, input: "CoffeeScript JavaScript javascript", groups: undefined]

上面匹配的是四个字母,这四个字母要满足以下条件:1⃣️、单词重复 4 次 2⃣️、紧跟着的应该是 Script 字符串,3⃣️、而且 Script 字符串应该是单词的结尾部分。

**零宽肯定先行断言的意思是:**现在有一段正则语法,用这段语法去匹配给定的文本。但是,满足条件的文本不仅要匹配这段语法,紧跟着它的必须是一个位置,这个位置又必须满足一段正则语法。**再直白点,**我要匹配一段文本,但是这段文本后面必须紧跟着另一段特定的文本。零宽肯定先行断言就是一个界碑,我要满足前面和后面所有的条件,但是我只要前面的文本。`

'CoffeeScript JavaScript javascript'.match(/\b\w{4}(?=Script\b)\w+/) // ["JavaScript", index: 13, input: "CoffeeScript JavaScript javascript", groups: undefined]

//上面的例子更加直观,零宽肯定先行断言已经匹配过Script一次了,后面的\w+却还是能匹配Script成功,足以说明它的零宽特性。它为紧贴在它前面的规则服务,并且不影响后面的匹配规则。

2、零宽肯定后行断言。语法:圆括号内最左边加上 ?<=

保留后面,前面为条件

'演员高圆圆 将军霍去病 演员霍思燕'.match(/(?<=演员)\S+/) // ["霍思燕", index: 14, input: "演员高圆圆 将军霍去病 演员霍思燕", groups: undefined]
// 一个正则可以有多个断言:
'演员高圆圆 将军霍去病 演员霍思燕'.match(/(?<=演员).+?(?=\s|$)/) // ["霍思燕", index: 14, input: "演员高圆圆 将军霍去病 演员霍思燕", groups: undefined]

3、零宽否定先行断言。语法:圆括号内最左边加上 ?!

以否定后面内容为条件,保留前面。

'TypeScript Perl JavaScript'.match(/\b\w{4}(?!Script\b)/) // ["Perl", index: 11, input: "TypeScript Perl JavaScript", groups: undefined]

4、零宽否定后行断言。语法:圆括号内最左边加上 ?<!

以否定前面内容为条件,保留后面。

'演员高圆圆 将军霍去病 演员霍思燕'.match(/(?<!演员)\S+/) // ["霍去病", index: 8, input: "演员高圆圆 将军霍去病 演员霍思燕", groups: undefined]

十一、修饰符

1、g (global:开启全局匹配模式)
2、i(ignoreCase:i 修饰符可以全局忽略大小写。)
3、m (multiline:多行匹配)这个修饰符有特定起作用的场景:它要和^和$搭配起来使用。默认情况下,^和$匹配的是文本的开始和结束,加上m修饰符,它们的含义就变成了行的开始和结束。
;`abc
xyz
`.match(/xyz/) // ["xyz", index: 5, input: "↵abc↵xyz↵", groups: undefined]
;`abc
xyz
`.match(/^xyz$/) // null
;`abc
xyz
`.match(/^xyz$/m) // ["xyz", index: 5, input: "↵abc↵xyz↵", groups: undefined]
4、y(sticky:)要求必须从文本的开始实施匹配,因为它会开启全局匹配,匹配到的文本的下一个字符就是下一次文本的开始。这就是所谓的粘连。
'a bag with a tag has a mag'.match(/\wag/g) // ["bag", "tag", "mag"]
'a bag with a tag has a mag'.match(/\wag/y) // null
'bagtagmag'.match(/\wag/y) // ["bag", index: 0, input: "bagtagmag", groups: undefined]
'bagtagmag'.match(/\wag/gy) // ["bag", "tag", "mag"]
5、s (singleline:修饰符的作用就是让.可以匹配任意单个字符。)s不是dotAll的缩写。s修饰符要和.搭配使用,默认情况下,.匹配除了换行符之外的任意单个字符,然而它还没有强大到无所不能的地步,所以正则索性给它开个挂。
;`abc
xyz
`.match(/c.x/) // null
;`abc
xyz
`.match(/c.x/s) // ["c↵x", index: 3, input: "↵abc↵xyz↵", groups: undefined]
6、u(unicode:)有一些Unicode字符超过一个字节,正则就无法正确的识别它们。u修饰符就是用来处理这些不常见的情况的。

十二、工具链接

解析正则表达式含义

正则表达式速查表

正则表达式详解