这是该系列的第15篇学习笔记!
8,后行断言
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>后行断言</title>
</head>
<body>
<script>
// JavaScript语言的正则表达式只支持“先行断言”和“先行否定断言”,不支持“后行断言”和“后行否定断言”
// 先行断言 --> x只有在y前面才匹配,必须写成“/x(?=y)/”的形式
// 例如,只匹配百分号之前的数字,要写成"/\d+(?=%)/"
// 先行否定断言 --> x只有不在y前面才匹配,必须写成“/x(?!y)/”的形式
// 例如,只匹配不在百分号之前的数字,要写成"/\d+(?!%)/"
console.log(/\d+(?=%)/.exec('100% of US presidents have been male')); // ['100']
console.log(/\d+(?!%)/.exec('that is all 44 of them')); // ['44']
// 互换正则表达式,则会匹配失败
// 另外,“先行断言”括号之中的部分(?=%)是不计入返回结果的
console.log(/\d+(?!%)/.exec('100% of US presidents have been male')); // ['10']
console.log(/\d+(?=%)/.exec('that is all 44 of them')); // null
// 后行断言 --> x只有在y后面才匹配,必须写成“/(?<=y)x/”的形式
// 例如,只匹配美元之后的数字,要写成"/(?<=\$)\d+/"
// 后行否定断言 --> x只有不在y后面才匹配,必须写成“/(?<!y)x/”的形式
// 例如,只匹配不在百分号之前的数字,要写成"/(?<!\$)\d+/"
console.log(/(?<=\$)\d+/.exec('Beinge is ont the $10323.23 bill'));
console.log(/(?<!\$)\d+/.exec("it's worth about %981212"))
// “后行断言”括号之中的部分(?<=\$)是不计入返回结果的
// “后行断言”的实现需要先匹配“/(?<=y)x/”的x,然后在回到左边匹配y的部分,这种“先右后左”的执行顺序与所有其他正则操作相反,导致了一些不符合预期的结果
// 代码捕获两个组匹配
console.log(/(?<=(\d+)(\d+))$/.exec('1053')); // [ "", "1", "053"] --> 有“后行断言”,由于执行顺序是从右到左,所以第二个括号是贪婪模式,第一个括号只能捕获一个字符
console.log(/^(\d+)(\d+)$/.exec('1053')); // ["1053", "105", "3"] --> 没有“后行断言”,第一个括号是贪婪模式,第二个括号只能捕获一个字符
// “后行断言”的反斜杠引用也与通常的顺序相反,必须放在对应的括号之前
console.log(/(?<=(o)d\1)r/.exec('hodor')); // null
console.log(/(?<=\1d(o))r/.exec('hodor')); // ['r', 'o']
// 上面代码中,后行断言的反斜杠引用(\1)必须放在前面才可以,放在括号后边不会得到匹配结果
// --> 后行断言是从左到右进行扫描,发现匹配后再回过头来从右到左完成反斜杠引用
</script>
</body>
</html>
9,Unicode属性类
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Unicode属性类</title>
</head>
<body>
<script>
// 【提案】引入一种新的类的写法(这两种类只对Unicode有效,所以使用的时候一定要加上u修饰符,否则,会报错)
// \p{...} and \P{...},允许正则表达式匹配符合Unicode某种属性的所有字符
const regexGreekSymbol = /\p{Script=Greek}/u;
console.log(regexGreekSymbol.test('n'));
// Unicode属性类要指定属性名和属性值
// \p{Script=Greek}
// 对于某些属性,可以只写属性名
// \p{UnicodePropertyName}
// \P{...}是\p{...}的反向匹配,即匹配不满足条件的字符
// 由于Unicode的属性非常多,所以这种新类的表达能力非常强
const regex = /^\p{Decimal_Number}+$/u;
console.log(regex.test('1234567890123456')); // true
// \p{Number}能匹配罗马数字
// 【end】
</script>
</body>
</html>
10,具名组匹配
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>具名组匹配</title>
</head>
<body>
<script>
// (1) 简介
// 正则表达式使用圆括号进行“组匹配”
// const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
// var matchObj = RE_DATE.exec('1992-12-23');
// console.log(matchObj[1]);
// console.log(matchObj[2]);
// console.log(matchObj[3]);
// 组匹配的问题:只能使用数字序号进行引用,要是组的顺序变了,引用的时候就必须修改序号
// 【提案】
// “具名组匹配”:允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用
// “具名组匹配”在圆括号内部,在模式的头部添加“问号+尖括号+组名”(?<year>),然后就可以在exec方法返回的结果的groups属性上引用该组名
// const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
// const matchObj = RE_DATE.exec('1998-12-30');
// const year = matchObj.groups.year;
// const month = matchObj.groups.month;
// const day = matchObj.groups.day;
// console.log(year, month, day);
// console.log(matchObj[1]);
// 具名组匹配等于为每一组匹配加上了ID,便于描述匹配的目的,如果组的顺序变了,也不用改变匹配后的处理代码
// 如果具名组没有匹配,那么对于的groups对象属性为undefined
// const RE_OPT_A = /^(?<as>a+)?$/;
// const matchObj = RE_OPT_A.exec('');
// console.log(matchObj.groups.as); // undefined
// console.log( 'as' in matchObj.groups); // true
// (2) 解构赋值和替换
// 有了具名组匹配,可以使用解构赋值直接从匹配结果上为变量赋值
let {groups:{one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
const result = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
console.log(typeof result.groups);
console.log(one);
console.log(two);
// 字符串的替换,使用$<组名>引用具名组(第二个参数可以是字符串或者函数)
// 第二个参数是字符串
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
console.log('2015-01-02'.replace(re, '$<day>/$<month>/$<year>'));
// 第二个参数是函数
// '2015-01-02'.replace(re, (
// matched,
// capture1,
// capture2,
// capture3,
// position,
// S,
// groups
// ) => {
// let {day, month, year} = args[args.length - 1];
// return `${day}/${month}/${year}`;
// });
// (3) 引用
// 如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法
// const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
// console.log(RE_TWICE.test('abc!abc')); // true
// console.log(RE_TWICE.test('abc!ab')); // false
// 数字引用(\1)依然有效
// const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
// console.log(RE_TWICE.test('abc!abc')); // true
// console.log(RE_TWICE.test('abc!ab')); // false
// 两种引用可以同时使用
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/;
console.log(RE_TWICE.test('abc!abc!abc')); // true
console.log(RE_TWICE.test('abc!abc!ab')); // false
</script>
</body>
</html>
让学习“上瘾”,成为更好的自己!!!