看懂火星文(三)

6次阅读

共计 4280 个字符,预计需要花费 11 分钟才能阅读完成。

感谢
本文参考《正则表达式迷你书》
分组和分支结构
分组
括号可以提供分组的功能。/a+/, 标示 a 出现多次。/(ab)+/, 则是将 ab 作为一组,表示 ab 出现多次。
分组引用
使用括号可以实现数据提取和替换操作。我们以匹配日期 (yyyy-mm-dd) 为例

// 无括号版本
var reg1 = /\d{4}-\d{2}-\d{2}/

// 有括号版本
var reg2 = /(\d{4})-(\d{2})-(\d{2})/

正则引擎在匹配的过程中,会存储每一个分组匹配到的数据
提取分组数据
match 方法
match 接受一个正则表达式作为参数。如果正则表达式中有 g 标示, 将返回与完整正则表达式匹配的所有结果,但不会返回捕获组。如果没有使用 g 标示,则仅返回第一个完整匹配及其相关的捕获组。
var regex = /(\d{4})-(\d{2})-(\d{2})/g
var string = “2017-06-12 2017-06-12”

// [“2017-06-12”, “2017-06-12”]
// 返回与完整正则表达式匹配的所有结果, 不含分组的结果
string.match(regex)

var regex = /(\d{4})-(\d{2})-(\d{2})/
var string = “2017-06-12 2017-06-12”

// 只返回第一个完整匹配和其分组
// [“2017-06-12”, “2017”, “06”, “12”, index: 0, input: “2017-06-12 2017-06-12”, groups: undefined]
string.match(regex)
exec 方法
exec 方法接受一个字符串作为参数。如果 exec 匹配失败将会返回 null, 如果匹配成功 exec 方法将会返回一个数组。
返回的数组将完全匹配成功的文本作为第一项,而分组匹配的结果在数组位置 0 的后面。返回的结果同时拥有 index 属性, 标示了匹配字符为于原始字符的索引。input 属性则是原始的字符串。
注意当正则对象是否包含 g 返回结果是不一样的。如果正则中含有 g 标示, 那么正则对象的 lastIndex(下一次匹配开始的位置), 会更新。而如果不含有 g, 正则的 lastIndex 不会更新。

var regex = /(\d{4})-(\d{2})-(\d{2})/
var string = “2017-06-12 2017-06-12”

// [“2017-06-12”, “2017”, “06”, “12”, index: 0, input: “2017-06-12 2017-06-12”, groups: undefined]
// regex.lastIndex === 0
regex.exec(string)
// [“2017-06-12”, “2017”, “06”, “12”, index: 0, input: “2017-06-12 2017-06-12”, groups: undefined]
// regex.lastIndex === 0
regex.exec(string)

var regex = /(\d{4})-(\d{2})-(\d{2})/g
var string = “2017-06-12 2017-06-12”

// [“2017-06-12”, “2017”, “06”, “12”, index: 0, input: “2017-06-12 2017-06-12”, groups: undefined]
regex.exec(string)
// [“2017-06-12”, “2017”, “06”, “12”, index: 11, input: “2017-06-12 2017-06-12”, groups: undefined]
// regex.lastIndex === 21
regex.exec(string)
RegEx
我们也可以通过 RegEx 全局的构造函数来获取分组匹配的结果。构造函数的全局属性 $1 到 $9 存储了分组匹配的结果。

var regex = /(\d{4})-(\d{2})-(\d{2})/g
var string = “2017-06-12 2017-06-12”

// 任何正则操作即可
regex.exec(string)

// “2017”
RegExp.$1
替换分组数据
我们可以通过 replace 方法配合分组匹配的结果, 实现替换分组数据
replace
str.replace(regexp|substr, newSubStr|func)

regexp 一个正则表达式对象,该正则所匹配的内容会被 replace 的第二个参数 (或者函数的返回值) 替换掉
substr 当 str 中含有 substr, 会被第二个参数替换, 仅有第一个匹配项会被替换
newSubStr 用于替换掉第一个参数在原字符串中的匹配部分的字符串。该字符串中可以内插一些特殊的变量名(见下表)
func 函数的返回值将替换掉第一个参数匹配到的结果

变量名
含义

$1, $2, $3……
第 n 个分组匹配的结果

$`
插入当前匹配的子串左边的内容

$’
插入当前匹配的子串右边的内容

$&
当前匹配的子串

// “????loHel????”
‘????Hello????’.replace(/(Hel)(lo)/, ‘$2$1’)

// “????ello????ello????”
// H 被替换为 ello????
‘????Hello????’.replace(/H/, “$'”)

// “????????ello????”
// H 被替换为????
‘????Hello????’.replace(/H/, “$`”)
指定一个函数作为参数
函数的返回值作为替换字符串。(注意:上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是,如果第一个参数是正则表达式,并且其为全局匹配模式,那么这个方法将被多次调用,每次匹配都会被调用。

// 函数每匹配到一次, 就会被调用
‘????????????????????’.replace(‘/????/g’, function () {
return ‘????’
})

函数的参数
含义

match
当前匹配的子串

p1, p2, …… pn
匹配的分组数据

示例
将 ’2019-01-01’ 替换为 01/01/2019
var str = ‘2019-01-01’

var reg = /(\d{4})-(\d{2})-(\d{2})/

// 01/01/2019
var result = str.replace(reg, ‘$2/$3/$1’)

// 01/01/2019
result = str.replace(reg, function (match, year, month, day) {
return `${month}/${day}/${year}`
})
反向引用
我们现在有一个需求, 使用一个正则表达式匹配如下格式的???? 日期字符串
2019-01-01, 2019.01.01, 2019/01/01, 并且前后的分割符号需要保持一致

var reg = /\d{4}(\/|\.|-)\d{2}(\/|\.|-)\d{2}/
???? 上面的正则达到了最基本的目的, 但是我们还没有解决前后分割符需要保持一致的问题。
这时我们可以到反向引用, 1 表示第一个分组所匹配到的具体的字符, 2, 3 同理

var reg = /\d{4}(-|\/|\.)\d{2}\1\d{2}/
括号嵌套
var regex = /^((\d)(\d(\d)))\1\2\3\4$/
var string = “1231231233”

// 分组一
// \d\d\d \1 对应 123
// 分组二
// \d \2 对应 1
// 分组三
// \d\d \3 对应 23
// 分组四
// \d \4 对应 3

10
10 表示第 10 个分组呢? 还是 1 和 0?
10 表示的是第十个分组
引用不存在的分组
引用不存在的分组不会报错。例如 2, 2 分组不存在, 2 匹配的就是字符串 ”2″
如果分组后面添加量词
分组后面有量词的话,分组最终捕获到的数据是最后一次的匹配
var str = ‘12345’
var reg = /\d+/
var reg1 = /(\d)+/

// [“12345”, index: 0, input: “12345”, groups: undefined]
str.match(reg)
// [“12345”, index: 0, input: “12345”, groups: undefined]
str.match(reg1)
// \1 应当是匹配的最后一次结果
var regex = /(\d)+ \1/;
// false
regex.test(“12345 1”)
// true
regex.test(“12345 5”) );
非捕获括号
之前的括号都被称为捕获型的分组, 捕获到的组的数据会在 API 中引用。如果只想使用括号最原始的功能,不在 API 和反向引用中使用可以使用非捕获括号。

var str = ‘HelloWorld’
var reg1 = /(?:Hello)(?:World)/
var reg2 = /(Hello)(World)/

// [“HelloWorld”, index: 0, input: “HelloWorld”, groups: undefined]
// 捕获组的数据不会引用
reg1.exec(str)
// [“HelloWorld”, “Hello”, “World”, index: 0, input: “HelloWorld”, groups: undefined]
reg2.exec(str)
示例
实现 trim 功能
第一种思路是匹配空格, 然后替换空格为空字符串

var reg = /^\s+|\s+$/g
var str = ‘ reg ‘
str.replace(reg, ”)
第二种思路是使用分组把内容提取出来, 进行替换。

var str = ‘ reg ‘
var reg = /^\s+(.*)\s+$/g

str.replace(reg, ‘$1’)
如果使用量词, 我们需要使用惰性匹配, 因为 (.)s$ 中(.) 的部分会匹配除了最后一个空格之外的其他空格

var str = ‘ reg ‘
var reg = /^\s*(.*)\s*$/g

str.replace(reg, ‘$1’)
将每个单词的首字母转换成大写

var str = ‘hello world’

// Hello World
// 我们匹配单词边界后的第一个字母
str.replace(/\b\w{1}/g, function (word) {
return word.toLocaleUpperCase()
})
匹配成对标签
我们可以使用反向引用, 我们将前面的标签作为分组用括号包起来, 1 将会是分组匹配的结果。我们这样就能保证前后两个标签的类型是一致的

var reg = /<([^>]+)>\w+<\/\1>/

// true
reg.test(‘<p>123</p>’)

// false
reg.test(‘<p>123</div>’)

正文完
 0