翼度科技»论坛 编程开发 JavaScript 查看内容

正则的扩展详解

9

主题

9

帖子

27

积分

新手上路

Rank: 1

积分
27
RegExp()

在es5中,RegExp的构造函数参数有两种情况
1、字符串
2、正则表达式
  1. // 第一种情况
  2. let regex = new RegExp('abc', 'i')
  3. // 第二种情况
  4. let regex2 = /abc/i
复制代码
这两种情况是等价的
  1. let s = 'abc'
  2. regex.test(s) == regex2.test(s); // true
复制代码
在es5中这两种方式不能混用,如果第一个参数是一个正则,第二个是修饰会报错
let regex3 = new RegExp(/abc/,'i')    // 报错
es6改变了这种行为,如果第一个是正则,第二个参数可以使用修饰符,并且返回的正则表达式会忽略原有的正则表达式修饰符,只使用新指定的修饰符。
  1. let regex4 = new RegExp(/abc/ig, 'i')
复制代码
在这里,第二个参数i会把原有的正则修饰符ig覆盖,最后的结果为/abc/i
u 修饰符

es6对正则表达式添加了u修饰符,表示为Unicode模式,用来处理大于\uFFFF的Unicode字符
  1. /^\uD83D/u.test('\uD83D\uDC2A');  // false
复制代码
这里加上了u.所以变成了es6支持,将\uD83D\uDC2A转译为一个字符,与\uD83D不匹配,所以返回false
  1. /^\uD83D/.test('\uD83D\uDC2A'); // true  
复制代码
这里没有加u.为es5支持,无法识别4个字节的UTF-16编码。会将\uD83D\uDC2A识别为两个字符,比对的时候其中的字符匹配,所以返回true
点字符 .

点字符在正则表达式中,表示除了换行符以外的任意单个字符,但对于码点大于0xFFFF的字符,点字符串无法识别,必须前面加上u修饰符
  1. let z = '正'
  2. /^.$/.test(z);   // true
  3. /^.$/u.test(z);  // true
复制代码
Unicode 字符表示法

es6新增了大括号表示unicode字符,这种字符在正则中必须使用 u 修饰符才能识别,否则会被解读为量词,量词是无法识别大于0xFFFF的unicode字符的。
  1. /\u{61}/.test('a'); // false
  2. /\u{61}/u.test('a'); // true
复制代码
i 修饰符

i 修饰符为不区分大小写,对于大于0xFFFF的unicode的修饰符,后面需要加上 u
  1. /[a-z]/i.test('\u212A'); // false
  2. /[a-z]/iu.test('\u212A'); // true
复制代码
y 修饰符

y修饰符,意为粘连(sticky)修饰符
在正则匹配中,g为全局匹配且没有位置限制。
y在正则匹配中,有位置限制,必须在上一次匹配完后的下一个字符串的第一位开始匹配。
  1. let a = 'aaa-aa-a'
  2. let r = /a+/y
  3. let r2 = /a+/g
  4. // 第一次匹配
  5. r.exec(a); // aaa
  6. r2.exec(a); // aaa
复制代码
  1. // 第二次匹配
  2. r.exec(a); // null  
  3. r2.exec(a); // aa  
复制代码
y为粘连,上一次匹配完后的下一个字符串第一位,第一位是:-无法匹配上,解决的方法:其实只需要修改一下正则保证每次匹配上就行了 /a+-/y
g为全局匹配没有位置限制
RegExp.prototype.sticky
来检查是否设置了 y 修饰符
  1. let a1 = /heelo\d/y
  2. console.log(a1.sticky); // true
复制代码
检测正则表达式修饰符以及正文


  • source 返回正则匹配规则
  • flags 返回正则的修饰符
  1. let rul = /abc/ig
  2. console.log(rul.source); // abc
  3. console.log(rul.flags); // ig
复制代码
s 修饰符 : dotAll模式

正则表达式中,点(.)是一个特殊的字符,代表任意字符,但是有两种例外
第一种:四个字节的UTF-16字符,这个使用 u 修饰符解决
第二种:行终止符,使用 s 修饰符解决
行终止符,就是该字符表示一行的终结,下面四个字符属于“行终止符”

  • U+000A 换行符(\n)
  • U+000D 回车符(\r)
  • U+2028 行分隔符(line separator)
  • U+2029 段分隔符(paragraph separator)
  1. let f = /foo.bar/.test('foo\nbar')
  2. console.log(f); // false // 因为.不匹配 \n ,所以正则表达式返回false
  3. // 解决方法 : s 修饰符
  4. /* 使用s修饰符,可以使 . 匹配任意单个字符 */
  5. let f2 = /foo.bar/s.test('foo\nbar')
  6. console.log(f2); // true
复制代码
这种s修饰符的模式被称为dotAll模式,dot就是代表一切字符。
所以,正则表达式还引入了dotAll模式,检查该表达式是否开启了dotAll模式。
  1. let f3 = /foo.bar/s
  2. console.log(f3.dotAll); // true  // 开始了dotAll模式
复制代码
先行断言和后行断言

先行断言

先行断言指的是,x在y的前面才匹配
语法:/x(?=y)/   x在y符号前才匹配
  1. let look = '100% 东方不败'
  2. /\d+(?=%)/.exec(look); // 100  // 数字在百分号前面才匹配
  3. let look2 = '100! 东方不败'
  4. console.log(/\d+(?=%)/.exec(look2));  // null 没有匹配到%前的数字
复制代码
后行断言


与先行断言相反,x在y的后面时才匹配
语法:/(?{        let {day,month,year} = groups;        return `${day}/${month}/${year}`       })console.log(data);   // 13-12-2022[/code]说明:

  • matched     // '2022-12-13' 匹配结果
  • params1    // 第一个组匹配2022
  • param2     // 第一个组匹配12
  • params3   // 第一个组匹配13
  • position    // 匹配开始的位置 0
  • sources   // 原字符串 2022-12-13
  • groups    // 具名组构成的一个对象
引用

如果要在正则表达式内部引用某个“具名组匹配”,可以使用 \k 的写法
  1. let look3 = '东方不败 $100 西方求败'
  2. /(?<=\$)\d+/.exec(look3); // 100  表示在\$后的字符才会被匹配
复制代码
其实可以这么理解,引用就是将前面定义好的组,拿过来复用。
数字引用
首先需要明确分组的概念,即正则中的()中的内容是一个分组,里面的内容作为一个整体引用。如果在分组后面接上 \数字,表示匹配第几个分组
  1. let rul = /(\d{4})-(\d{2})-(\d{2})/
  2. let r = rul.exec('2022-12-13')
  3. console.log(r[1]);  // 2022
  4. console.log(r[2]);  // 12
  5. console.log(r[3]);  // 13
复制代码
第一个输出:问题出在结尾是英文,这里结尾使用了\数字,1表示匹配第一个分组,第一个分组是必须是2个数字,这里的结尾用的英文所以返回false
第二个输出:true,其实这时的正则为/^(\d{2})([a-z]+)(\d{2})$/,\1引用了第一个组

具名匹配的引用和数字引用可以同时使用
  1. let rul2 = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
  2. let r2 = rul2.exec('2022-12-13')
  3. console.log(r2.groups.year);  // 2022
  4. console.log(r2.groups.month);  // 12
  5. console.log(r2.groups.day);  // 13
  6. console.log(r2.groups.minute);  // undefined  如果没有匹配,那么对象属性为undefined
复制代码
第一个输出:这里使用了引用以及数字引用
第二个输出:false, 结尾通过数字引用后,应该为数字\d{2}
d 修饰符:正则匹配索引

es2022新增d修饰符,可以在exec()、match()的返回结果添加indices属性,在该属性上可以拿到匹配的开始位置和结束位置,下标从0开始
  1. let {groups : {hours,minute}} = /^(?<hours>.*):(?<minute>.*)/.exec('12:49')
  2. console.log(hours,minute);  // 12  49  // 通过解构赋值提取
复制代码
非常重要!!!这里需要注意的是,匹配的字符串开始位置是字符串的第一个值,匹配的结束位置,并不在返回结果中,正确来说,匹配的结束位置是匹配字符串的末尾的下一个位置,如果这一句不理解,下面的内容将会很难理解
如果正则中包含组匹配,那么indices属性对应的数组就会包含多个成员,并且提供每个组的开始位置和结束位置
  1. let rul4 = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
  2. console.log('2022-12-13'.replace(rul4,'$<day>/$<month>/$<year>')); // 13-12-2022
  3. console.log(rul4);  // /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
复制代码

  • 这里的匹配并不是单个的匹配,而是整体,最外层的bc其实不参与单独的匹配,而是放到bcde整体中,所以第一个数组打印的是1,6,b的下标为1,e的下一个字符的下标为6`
  • 组会单独进行匹配,d的下标为4,e的下一个字符的下标为6

多个组匹配以及下标
  1. let data = '2022-12-13'.replace(rul4,(matched,params1,param2,params3,position,sources,groups)=>{
  2.         let {day,month,year} = groups;
  3.         return `${day}/${month}/${year}`
  4.        })
  5. console.log(data);   // 13-12-2022
复制代码
匹配的顺序:bcdefg、defg、fg,对应的打印结果[1,8][4,8][6,8]

如果正则表达式包含具名匹配,那么indices.groups的属性会是一个对象,可以从该对象获取具名组匹配的开始位置和结束位置
  1. let str = /^(?<word>[a-z]+)!\k<word>$/
  2. str.test('abc!abc');  // true  引用了具名匹配,前后都是abc,前后字符一致
  3. str.test('abc!ab');   // false 引用了具名匹配,前后字符不匹配,但是第二个少一个c
复制代码


如果未匹配上则打印undefined
String.prototype.matchAll()

matchAll()可以一次性取出所有匹配,返回的是遍历器(Iterator)而不是数组
  1. let str2 = /^(\d{2})([a-z]+)\1$/
  2. console.log(str2.test('11abcd'));  // false
  3. console.log(str2.test('11aa11'));  // true
复制代码

转为数组的方法一
  1. let str3 = /^(?<word>[a-z]+)-(\d{2})-\k<word>-\2$/
  2. str3.test('a-11-a-11'); // true
  3. str3.test('a-11-a-aa'); // false
复制代码
转为数组的方法二
  1. let text = 'abcdefg'
  2. let te = /bc/d
  3. let result = te.exec(text)
  4. result.index;  // 1   拿到bc匹配的起始位置,下标为1
  5. result.indices;  // [1,3]  拿到bc匹配的开始和结束位置的下一个字符的位置
复制代码

案例源码:https://gitee.com/wang_fan_w/es6-science-institute
如果觉得这篇文章对你有帮助,欢迎点亮一下star哟

来源:https://www.cnblogs.com/wang-fan-w/p/17136895.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具