1.用一个正则表达式来得到所有匹配项。

目前,您可以通过几种方式获取给定正则表达式的所有匹配项。

1. RegExp.prototype.exec() 与 /g

如果正则表达式有/g标志,那么多次调用.exec()就会得到所有匹配的结果。如果没有匹配的结果,.exec()就会返回null。在这之前会返回每个匹配的匹配对象。这个对象包含捕获的子字符串和更多信息。

举个例子:得到所有双引号之间的字符串

function collectGroup1(regExp, str) { const matches = []; while (true) { const match = regExp.exec(str); if (match === null) break; // 把match中捕获的字符串,加到matches中。 matches.push(match[1]); } return matches; } // /"([^"]*)"/ug 匹配所有双引号与其之间的内容,并捕获所有引号间的信息。 collectGroup1(/"([^"]*)"/ug,`"foo" and "bar" and "baz"`); // [ 'foo', 'bar', 'baz' ] 

如果正则表达式没有/g标志,.exec()总是返回第一次匹配的结果。

> let re = /[abc]/;
> re.exec('abc')
[ 'a', index: 0, input: 'abc' ] > re.exec('abc') [ 'a', index: 0, input: 'abc' ] 

这样的话对函数collectGroup1就是一个坏消息,因为如果没有/g标志,函数无法结束运行,此时match就一直是第一次匹配的结果,循环永远无法break。

为什么会这样?

因为正则表达式有一个lastIndex(初始值为0)属性,每次.exec()前,都会根据lastIndex属性的值来决定开始匹配的位置。

如果正则表达式没有/g标志,那么运行一次.exec()时,不会改变lastIndex的值,导致下一次运行exec()时,匹配仍旧是从字符串0的位置开始。

当正则表达式加了/g标志后,运行一次exec(),正则表达式的lastIndex就会改变,下次运行exec()就会从前一次的结果之后开始匹配。

2.String.prototype.match() 与 /g

你可以使用.match()方法和一个带有/g标志的正则表达式,你就可以得到一个数组,包含所有匹配的结果(换句话说,所有捕获组都将被忽略)。

> "abab".match(/a/ug)
[ 'a', 'a' ] 

如果/g标志没有被设置,那么.match()与RegExp.prototype.exec()返回的结果一样。

> "abab".match(/a/u)
[ 'a', index: 0, input: 'abab' ] 

3.String.prototype.replace() 与 /g

你可以用一个小技巧来收集所有的捕获组——通过.replace()。replace函数接收一个能够返回要替换的值的函数,这个函数能够接收所有的捕获信息。但是,我们不用这个函数去计算替换的值,而是在函数里用一个数组去收集感兴趣的数据。

function collectGroup1(regExp, str) { const matches = []; function replacementFunc(all, first) { matches.push(first); } str.replace(regExp, replacementFunc); return matches; } collectGroup1(/"([^"]*)"/ug,`"foo" and "bar" and "baz"`); // [ 'foo', 'bar', 'baz' ] 

对于没有/g标志的正则表达式,.replace()仅访问第一个匹配项。

4.RegExp.prototype.test()

.test()只要正则表达式匹配成功就会返回true。

const regExp = /a/ug;
const str = 'aa';
regExp.test(str); // true regExp.test(str); // true regExp.test(str); // false 

5.String.prototype.split()

你可以拆分一个字符串并用一个正则表达式去指定分隔符。如果正则表达式包含至少一个捕获组,那么.split()将会返回一个数组,其中结果会跟第一个捕获组互相交替。

const regExp = /<(-+)>/ug;
const str = 'a<--->b<->c';
str.split(regExp);
// [ 'a', '---', 'b', '-', 'c' ] const regExp = /<(?:-+)>/ug; const str = 'a<--->b<->c'; str.split(regExp); //[ 'a', 'b', 'c' ] 

2.目前这些方法存在的问题

目前这些方法都有以下几个缺点:

1.它们是冗长且不直观的。

2.如果标志/g被设置了,它们才会工作。有时候,我们会从其他地方接收正则表达式,比如通过一个参数。如果我们想要去确定所有匹配的项都能找到,那么不得不检查/g标志有没有被设置。

3.为了跟踪进程,所有的方法(除了match)改变了正则表达式的属性,.lastIndex记录了上一次匹配的结束为止。这使得在多个为止使用相同的正则表达式会存在风险(因为正则表达式的lastIndex属性改变了,但是你还在别的地方使用这个正则表达式,那么结果可能会和你想要的不一样)。这太可惜了,当你需多次调用.exec()的时候,你不能在一个函数内联一个正则表达式。(因为每次调用,正则表达式都会之重置)。

4.由于属性.lastIndex决定在了在哪继续调用。当我们开始继续收集匹配项的时候,就必须把她始终为0。但是,至少.exec()和其他一些方法会在最后一次匹配后将.lastIndex重置为0。如果它不是零,就会发生这种情况:

const regExp = /a/ug;
regExp.lastIndex = 2;
regExp.exec('aabb'); // null 

3.提案:tring.prototype.matchAll

这就是你调用.matchAll()的方式:

const matchIterator = str.matchAll(regExp);

给定一个字符串和一个正则表达式,.matchAll()为所有匹配的匹配对象返回一个迭代器。

你也可以使用一个扩展运算符...把迭代器转换为数组。

> [...'-a-a-a'.matchAll(/-(a)/ug)]
[ [ '-a', 'a' ], [ '-a', 'a' ], [ '-a', 'a' ] ] 

现在是否设置/g,都不会有问题了。

> [...'-a-a-a'.matchAll(/-(a)/u)]
[ [ '-a', 'a' ], [ '-a', 'a' ], [ '-a', 'a' ] ] 

使用.matchAll(),函数collectGroup1() 变得更短更容易理解了。

function collectGroup1(regExp, str) { let results = []; for (const match of str.matchAll(regExp)) { results.push(match[1]); } return results; } 

我们可以使用扩展运算符和.map()来使这个函数更简洁。

function collectGroup1(regExp, str) { let arr = [...str.matchAll(regExp)]; return arr.map(x => x[1]); } 

另一个选择是使用Array.from(),它会同时转换数组和映射。因此,你不需要再定义中间值arr。

function collectGroup1(regExp, str) { return Array.from(str.matchAll(regExp), x => x[1]); } 

3.1 .matchAll() returns an iterator, not a restartable iterable

.matchAll()返回一个跌倒器,但不是一个真的可重新利用的迭代器。一旦结果耗尽,你不得不再次调用方法,获取一个新的迭代器。

相反,.match()加上 /g 返回一个迭代器即数组,只要你想,你就可以迭代它。

4.Implementing .matchAll()

你如何实现matchAll:

function ensureFlag(flags, flag) { return flags.includes(flag) ? flags : flags + flag; } function* matchAll(str, regex) { const localCopy = new RegExp( regex, ensureFlag(regex.flags, 'g')); let match; while (match = localCopy.exec(str)) { yield match; } } 

制作一个本地副本,确保了一下几件事:

  • /g被设置了
  • regex.index 不会改变
  • regex.index 是0

使用matchAll():

const str = '"fee" "fi" "fo" "fum"';
const regex = /"([^"]*)"/;

for (const match of matchAll(str, regex)) {console.log(match[1]); } // Output: // fee // fi // fo // fum 

5.常见问题

5.1 为什么不是RegExp.prototype.execAll()?

一方面,.matchAll()确实跟批量调用.exec()的工作很像,因此名称.execAll()会有意义。

另一方面,exec()改变了正则表达式,而match()没有。这就解释了,为什么名字matchAll()会被选择。

作者:打铁大师
链接:https://www.jianshu.com/p/7dbf4a1e6805
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

转载于:https://www.cnblogs.com/xbd-2018/p/10275308.html

String.prototype.matchAll 正则一些常用方法相关推荐

  1. 【ES11(2020)】String 扩展 String.prototype.matchAll()

    matchAll()方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器. const regexp = /t(e)(st(\d?))/g; const str = 'test1test2'; ...

  2. ES11新特性_String.prototype.matchAll方法---JavaScript_ECMAScript_ES6-ES11新特性工作笔记063

    然后我们再来看一下这个,es11提供的新特性,String.prototype属性的.matchAll方法 我们看看这个正则之前我们写过了,这个s 在最后添加了一个s 这个s表示模式符,这样加上以后, ...

  3. [Java]String类基础知识与常用方法总结

    这篇文章用于记录个人学习过程中Java中String类的一些基础知识和方法.主要记录了String类的特性.常用方法,以及和基本数据类型.包装类互转方面的内容. 源码部分 我们先简易看下Java13中 ...

  4. String.prototype.substr()

    概述 substr() 方法返回字符串中从指定位置开始到指定长度的子字符串. 语法 str.substr(start[, length]) 参数 start 开始提取字符的位置.如果为负值,则被看作  ...

  5. String.prototype 几个简洁的字符处理函数 (转)

    都是基于 String.prototype 的扩展: 起因是有个网友和我讨论两个函数, 一个是 isDateTime (判断字符是否是符合 yyyy-mm-dd hh:mm:ss日期格式) 另一个是 ...

  6. es6 --- String.prototype.padStart

    从实际出发理解. 首先看下面代码 // js var dt = new Date(); console.log(dt); 下面想把时间格式化,写一个dateFormat函数 // js functio ...

  7. String.prototype.padStart()

    String.prototype.padStart() ES6字符串新方法:填充字符串 String.prototype.padStart(maxLength,fillString='') Strin ...

  8. [转]String.prototype使用

    以下为web开发中常用到的js方法:收集并整理出来:简单的就不写出来了  1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transition ...

  9. ES10:Object.fromEntries()、String.prototype.trim()、Array.prototype.flat()、Array.prototype.flatMap()

    Object.fromEntries(iterable) 把键值对列表(二维数组)转换为一个对象,这个方法和 Object.entries() 相对 Object.fromEntries([['foo ...

最新文章

  1. rsync+inotify实时备份
  2. 查看Linux硬件信息命令的使用
  3. 课堂练习---统计空格流程图、Jackson图
  4. Angular 项目里 angular.json 文件内容的学习笔记
  5. linux安装python2环境_Python基础手册 2 —— Python 环境搭建(Linux)
  6. swift 4.2 - 根据字符串 push指定控制器
  7. c语言 intptr_t
  8. php 阻止ip代码,php – 如何阻止100,000个独立的IP地址
  9. Django菜鸟教程
  10. 使用极光推送(www.jpush.cn)向安卓手机推送消息【服务端向客户端主送推送】C#语言...
  11. 电路实验一阶电路误差分析_动态电路分析
  12. 商场管理系统实战项目
  13. spider pi 智能视觉六足机器人 开箱介绍 0602
  14. Ubuntu和本机之间复制粘贴
  15. CYUSB3014 USB3.0与FPGA设计
  16. (山理工 1243)母牛问题(大牛生小牛,小牛生。。。。)
  17. MFC应用程序“生死因果”内幕
  18. [转载]Python兵器谱
  19. 物联网将IT安全推向边缘
  20. JVM虚拟机读书日记4

热门文章

  1. Discuz蜘蛛统计seo插件-蜘蛛统计插件
  2. LOJ #10005. 「一本通 1.1 练习 1」数列极差
  3. 10005---海量数据排序总结
  4. Eclipse集成Mybatis Generator及应用
  5. 《那些年啊,那些事——一个程序员的奋斗史》——40
  6. CDH和Hadoop的区别
  7. mysql查询IN索引无效的问题【已解决】
  8. 【LittleXi】sql学习笔记
  9. 关于halcon深度图转灰度图
  10. 五种永久免费 内网穿透傻瓜式使用