现有一个字符串: “1-apple”
需要提取出 1 和 apple 来,对应的正则表达式很简单: ^(\d)-(.+)$
对应的代码也比较简单:
const str = "1-apple"
const regexp = /^(\d)-(.+)$/
let match = regexp.exec(str)
console.log(match[0])
console.log(match[1])
console.log(match[2])
其中,match[1]和match[2]是我们想要的结果,分别是 ‘1’ 和 ‘apple’ ,
而 match[0] 一般来说没什么用,它相当于一个完整匹配,值是 ‘1-apple’ 。
所谓“分组”,就是比如这种情况:
1-apple
2-orange
3-pear
需要正则的内容不止一行,和上例相比,其对应的正则表达式基本一样,但是后面必须加上gm标识符,
(g代表global,m代表multi line),代码如下:
const str = "1-apple\n2-orange\n3-pear"
const regexp = /^(\d)-(.+)$/gm //注意:和上例相比,唯一的区别是后面加了gm标志
let match = regexp.exec(str)
console.log(match[0])
console.log(match[1])
console.log(match[2])
直接运行代码,发现只解析了1-apple出来,后两行无效,这是为啥?
让我们修改一下代码:
const str = "1-apple\n2-orange\n3-pear"
const regexp = /^(\d)-(.+)$/gm
let match
match = regexp.exec(str)
console.log('index = ', match.index)
console.log('result = ', match)
match = regexp.exec(str)
console.log('index = ', match.index)
console.log('result = ', match)
match = regexp.exec(str)
console.log('index = ', match.index)
console.log('result = ', match)
在这个例子里,我们连续三次触发了match = regexp.exec(str)
这行语句,
事实上,尽管语句完全一样,但是每一次match的返回值都不同。在分组正则的时候,.exec方法总是会将当前匹配值的首字符位置保存在index变量里,当下一次触发.exec的时候,index并不会从0开始搜索,而是从第一次匹配完成之后的位置进行第二次匹配,如此反复,直至将整个字符匹配完成为止。
知道了 .exec 这个方法可以反复执行这个小秘密之后,将代码再改改就很简单了:
const str = "1-apple\n2-orange\n3-pear"
const regexp = /^(\d)-(.+)$/gm //注意:和上例相比,唯一的区别是后面加了gm标志
let match
while ((match = regexp.exec(str)) !== null) {
console.log('index = ', match.index)
console.log('result = ', match)
}
如果你不喜欢while循环方式,还可以使用 matchAll ,就可以不必使用 while 循环加 exec 方式。使用 matchAll 会得到一个迭代器的返回值,配合 for…of, array spread, 或者 Array.from() 可以更方便实现功能。
const str = "1-apple\n2-orange\n3-pear"
const regexp = /^(\d)-(.+)$/gm
const matches = str.matchAll(regexp) // 采用matchAll进行匹配
for (const match of matches) { // 采用 for of 方式读取
console.log('index = ', match.index)
console.log('result = ', match)
}
这段代码效果和上例完全一样, 也可以用Array.from() 实现同样的效果:
const str = "1-apple\n2-orange\n3-pear"
const regexp = /^(\d)-(.+)$/gm
const matches = str.matchAll(regexp)
Array.from(str.matchAll(regexp), (match) => { // 采用 Array.from方式读取
console.log('index = ', match.index)
console.log('result = ', match)
})
或者更简单的ES6的“三个点”语法( array spread ):
const str = "1-apple\n2-orange\n3-pear"
const regexp = /^(\d)-(.+)$/gm
const matches = [...str.matchAll(regexp)] // 采用 ... 方式展开str.matchAll(regexp)
console.log(matches[0][1]) // 显示:1
console.log(matches[0][2]) // 显示:apple
console.log(matches[1][1]) // 显示:2
console.log(matches[1][2]) // 显示:orange
三个点称为 “array spread” 或者“展开语法”,它的作用很多很杂,没有展开语法的时候,只能组合使用 push, splice, concat 等方法,来将已有数组元素变成新数组的一部分。有了展开语法,通过字面量方式,构造新数组会变得更简单、更优雅:
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
// ["head", "shoulders", "knees", "and", "toes"]
本文只列举了非常简单的关于正则分组的基础案例,进一步研究可以阅读以下资料:
【matchAll()详解】:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll
【array spread 展开语法详解】:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax