Appearance
正则扩展
RegExp 构造函数
ES5 - RegExp 有两种形式构造参数
字符串 + 修饰符
js
var regex = new RegExp('xyz', 'i')
// 等价于
var regex = /xyz/i正则表达式
js
var regex = new RegExp(/xyz/i)
// 等价于
var regex = /xyz/iES6 - 引入第三种情况,正则表达式 + 修饰符
js
var regex = new RegExp(/abc/gi, 'i')
regex.flags // i
flag会覆盖原有修饰符
字符串正则方法
ES5 - match()、replace()、search()、split()
ES6 - 将 String 对象方法关联到 RegExp 对象
String.prototype.match调用RegExp.prototype[Symbol.match]String.prototype.replace调用RegExp.prototype[Symbol.replace]String.prototype.search调用RegExp.prototype[Symbol.search]String.prototype.split调用RegExp.prototype[Symbol.split]
ES2020 - String.prototype.matchAll():一次性取出所有匹配。
返回值:遍历器(Iterator),而不是数组。
js
const string = 'test1test2test3'
const regex = /t(e)(st(\d?))/g
for (const match of string.matchAll(regex)) {
console.log(match)
}
// ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
// ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
// ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]u 修饰符
u修饰符:用来正确处理大于\uFFFF的 Unicode 字符
js
/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // truey 修饰符
y 修饰符(粘连修饰符):全局匹配中后一次匹配都从上一次匹配成功的下一个位置开始。
g 修饰符 在剩余位置中取
lastIndex属性:每次搜索开始位置
js
var s = 'aaa_aa_a'
var r1 = /a+/g
var r2 = /a+/y
r1.exec(s) // ["aaa"] r1.lastIndex = 0
r1.exec(s) // ["aa"] r1.lastIndex = 3
r2.exec(s) // ["aaa"] r2.lastIndex = 0
r2.exec(s) // null r2.lastIndex = 3js
const REGEX = /a/gy
'aaxa'.replace(REGEX, '-') // '--xa'js
'a1a2a3'.match(/a\d/y) // ["a1"]
'a1a2a3'.match(/a\d/gy) // ["a1", "a2", "a3"]==@DIF== -
y不会忽略非法字符
s 修饰符:dotAll 模式
·:代表任意单个字符。存在两个例外
- UTF-16 字符
- 行终止符,如下
- U+000A 换行符(
\n) - U+000D 回车符(
\r) - U+2028 行分隔符(line separator
- U+2029 段分隔符(paragraph separator)
js
/foo.bar/.test('foo\nbar')
// false
/foo[^]bar/.test('foo\nbar')
// trueES2018 - s修饰符:使得.可以匹配任意单个字符
js
// es2018
;/foo.bar/s.test('foo\nbar') // true这被称为dotAll模式,即点(dot)代表一切字符。
dotAll属性:正则表达式是否处在dotAll模式
js
const re = /foo.bar/s
// 另一种写法
// const re = new RegExp('foo.bar', 's');
re.test('foo\nbar') // true
re.dotAll // true
re.flags // 's'RegExp.prototype.unicode 属性
unicode属性:表示是否设置了u修饰符
js
const r1 = /hello/
const r2 = /hello/u
r1.unicode // false
r2.unicode // trueRegExp.prototype.sticky 属性
sticky属性:表示是否设置了y修饰符
js
var r = /hello\d/y
r.sticky // trueRegExp.prototype.flags 属性
flags属性:会返回正则表达式的修饰符
js
// ES5 的 source 属性
// 返回正则表达式的正文
;/abc/gi.source /
// "abc"
// ES6 的 flags 属性
// 返回正则表达式的修饰符
abc /
ig.flags
// 'gi'先行断言和后行断言
先行断言 :x只有在y前面才匹配,必须写成/x(?=y)/
js
;/\d+(?=%)/.exec('100% of US presidents have been male') // ["100"]先行否定断言:x只有不在y 前面才匹配,必须写成/x(?!y)/
js
;/\d+(?!%)/.exec('that’s all 44 of them') // ["44"]ES2018 - 引入
后行断言:x只有在y后面才匹配,必须写成/(?<=y)x/
js
;/(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill') // ["100"]后行否定断言 :x只有不在y后面才匹配,必须写成/(?<!y)x/
js
;/(?<!\$)\d+/.exec('it’s is worth about €90') // ["90"]后行断言组匹配:先匹配/(?<=y)x/的x,然后再回到左边,匹配y的部分,先右再左
js
// 匹配在(\d+)(\d+)后面的串,第2个(\d+)先贪婪匹配
;/(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"]
// 第1个(\d+)先贪婪匹配
;/^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"]上面第一个匹配项都是贪婪匹配,053 和 105 ,只是顺序不同
补充:/(?<=(\d+)(\d+))$/ - 整个表达式都为断言,所以未匹配到串
js
;/(?<=(\d+)(\d+))1$/.exec('1053') // ["1", "0", "53"]反斜杠引用,也与通常的顺序相反
js
;/(?<=(o)d\1)r/.exec('hodor') // null
;/(?<=\1d(o))r/.exec('hodor') // ["r", "o"]补充:\1 表示 第一个捕获值。后行断言从右往左,第一行代码,\1 在 (\d) 之前(捕获值在 捕获之前),所以无法匹配。
Unicode 属性类
具名组匹配
使用圆括号进行组匹配
js
const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/
const matchObj = RE_DATE.exec('1999-12-31')
const year = matchObj[1] // 1999
const month = matchObj[2] // 12
const day = matchObj[3] // 31ES2018 - 具名组匹配
语法:?<组名>
为每一组匹配指定一个名称,未匹配值为undefined
js
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const matchObj = RE_DATE.exec('1999-12-31')
const year = matchObj.groups.year // "1999"
const month = matchObj.groups.month // "12"
const day = matchObj.groups.day // "31"
// const {groups: {year, month}} = RE_DATE.exec('1999-12-31');引用组名
语法:$<组名>
js
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
// params#2 String
'2015-01-02'.replace(re, '$<day>/$<month>/$<year>')
// '02/01/2015'
// params#2 Function
'2015-01-02'.replace(
re,
(
matched, // 整个匹配结果 2015-01-02
capture1, // 第一个组匹配 2015
capture2, // 第二个组匹配 01
capture3, // 第三个组匹配 02
position, // 匹配开始的位置 0
S, // 原字符串 2015-01-02
groups // 具名组构成的一个对象 {year, month, day}
) => {
let { day, month, year } = groups
return `${day}/${month}/${year}`
}
)内部应用
语法:k<组名>
正则表达式内部引用,也可以用 \1进行捕获
js
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/
RE_TWICE.test('abc!abc!abc') // true
RE_TWICE.test('abc!abc!ab') // false正则匹配索引
exec()返回 index 属性:获取整个匹配结果开始位置。
问题:包含组匹配,无法拿到每个组的开始位置
js
const text = 'zabbcdef'
const re = /ab+(cd(ef))/
const result = re.exec(text)
result.index // 1
result.indices // [ [1, 8], [4, 8], [6, 8] ]具名匹配:indices 会包含 groups 属性
js
const text = 'zabbcdef'
const re = /ab+(?<Z>cd)/
const result = re.exec(text)
result.indices.groups // { Z: [ 4, 6 ] }