【问题标题】:Trying to matchAll a regex on a JavaScript string尝试在 JavaScript 字符串上匹配所有正则表达式
【发布时间】:2019-06-20 15:59:41
【问题描述】:

我正在尝试string.matchAll以下字符串:

const text = 'textA [aaa](bbb) textB [ccc](ddd) textC'

我想匹配以下内容:

  • 第一:"textA [aaa](bbb)"
  • 第二:" textB [ccc](ddd)"
  • 第三:" textC"

注意: 捕获组已经存在于regex 中。这就是我需要的。

它几乎可以工作,但到目前为止我想不出一种方法来匹配字符串的最后一部分,即" textC",并且没有[*](*) 模式。

我做错了什么?

const text = 'textA [aaa](bbb) textB [ccc](ddd) textC'
const regexp = /(.*?)\[(.+?)\]\((.+?)\)/g;

const array = Array.from(text.matchAll(regexp));
console.log(JSON.stringify(array[0][0]));
console.log(JSON.stringify(array[1][0]));
console.log(JSON.stringify(array[2][0]));

更新:

除了以下答案中提供的良好解决方案之外,这也是一种选择:

const text= 'textA [aaa](bbb) textB [ccc](ddd) textC'

const regexp = /(?!$)([^[]*)(?:\[(.*?)\]\((.*?)\))?/gm;

const array = Array.from(text.matchAll(regexp));

console.log(array);

【问题讨论】:

  • 试试这个:(\w+)\s*(?:[(.+?)]((.+?)))?
  • (.+\)) (.+\)) (.+) 有什么问题吗?
  • My answer 将用于拆分具有任何模式的任何字符串,同时将匹配的文本保留在左侧拆分块中。它对你有用吗?您确定您需要的结果是您在问题中显示的结果吗? textC 是占位符吗,它可以等于 word 1 word 2 and word 3 and so on.... 并且您需要将此文本作为结果数组中的单个项目获取?

标签: javascript regex


【解决方案1】:

这是因为没有第三场比赛。在前两个匹配之后,字符串中只剩下“text C”了:

https://regex101.com/r/H9Kn0G/1/

要解决此问题,请将整个第二部分设为可选(还要注意最初的 \w 而不是 . 以防止该点吃掉整个字符串,以及用于围绕可选的“仅分组”括号部分,使您的匹配组保持不变):

(\w+)(?:\s\[(.+?)\]\((.+?)\))?

https://regex101.com/r/Smo1y1/2/

【讨论】:

  • 字字符对我来说太严格了。我想匹配任何字符串,后跟模式[+](+),如果多个模式[+](+)一个接一个地写在一起,我想一一匹配。
  • 要匹配直到下一个括号的任何内容,试试这个:((?:(?!\[).)+)(?:\s?\[(.+?)\]\((.+?)\))?regex101.com/r/HqbTpU/1我添加了一个带有负前瞻的“缓和令牌”,显然更复杂。
  • @ScottWeaver 当您使用单个字符限制 . 时,请不要使用经过调和的贪婪令牌。 (?:(?!\[).)+(几乎)=[^[]+。实际上,它等于[^[\n\r]+。否定字符类的工作速度要快得多。
  • 是的,这有点简单,而且工作方式相同。 regex101.com/r/vMFKXH/1
  • 此外,your regex solution is easy to break 如果在 [...](...) 构造之前有“独立”括号。
【解决方案2】:

方案一:通过匹配拆分

您可以通过匹配模式并从前一个索引获取子字符串直到匹配结束来进行拆分:

const text = 'textA [aaa](bbb) textB [ccc](ddd) textC'
const regexp = /\[[^\][]*\]\([^()]*\)/g;
let m, idx = 0, result=[];
while(m=regexp.exec(text)) {
  result.push(text.substring(idx, m.index + m[0].length).trim());
  idx = m.index + m[0].length;
}
if (idx < text.length) {
  result.push(text.substring(idx, text.length).trim())
}
console.log(result);

注意:

  • \[[^\][]*\]\([^()]*\) 匹配[,除[] 之外的任何0+ 个字符(与[^\][]*),然后是](,然后是除() 之外的0+ 个字符(与@987654333) @),然后是 )(参见 regex demo
  • 捕获组已删除,但如果需要,您可以将它们恢复并单独保存在生成​​的数组中(或保存在另一个数组中)
  • 添加.trim() 以消除前导/尾随空格(如果不需要,请删除)。

解决方案 2:匹配可选模式

我们的想法是匹配您拥有的模式之前的任何字符,然后匹配您的模式或字符串结尾:

let result = text.match(/(?!$)(.*?)(?:\[(.*?)\]\((.*?)\)|$)/g);

如果字符串可以有换行符,请将. 替换为[\s\S],或者考虑这种模式:

let result = text.match(/(?!$)([\s\S]*?)(?:\[([^\][]*)\]\(([^()]*)\)|$)/g);

the regex demo

JS 演示:

const text = 'textA [aaa](bbb) textB [ccc](ddd) textC'
const regexp = /(?!$)(.*?)(?:\[(.*?)\]\((.*?)\)|$)/g;

const array = Array.from(text.matchAll(regexp));
console.log(JSON.stringify(array[0][0]));
console.log(JSON.stringify(array[1][0]));
console.log(JSON.stringify(array[2][0]));

正则表达式详细信息

  • (?!$) - 不在字符串末尾
  • (.*?) - 第 1 组:尽可能少的除换行符以外的任何 0+ 字符(如果可能存在换行符,请更改为 [\s\S]*?,或者添加 s 修饰符,因为您的目标是 ECMAScript 2018)
  • (?:\[(.*?)\]\((.*?)\)|$) - 两种选择之一:
    • \[(.*?)\]\((.*?)\) - [,第 2 组:除换行符之外的任何 0+ 字符尽可能少,](,第 3 组:除换行符之外的任何 0+ 字符尽可能少,@987654349 @
    • | - 或
    • $ - 字符串结束。

【讨论】:

  • 抱歉,我花了太长时间才给出反馈。您的回答似乎太有用了(它缺少textBtextC 中的一些空格),但我的主要问题是它对我来说似乎不太可读。我希望更好地使用regexmatchAll 方法。谢谢。
  • 我有这个regex,它基本上可以工作,但它匹配最后一个位置的零长度匹配。 /([^\[]*)?(?:\[(.+?)\]\((.+?)\))?/gm
  • @cbdev420 那么,您想使用不可读的正则表达式解决方案吗? :) /(?=[\s\S])([\s\S]*?)(?:\[([^\][]*)\]\(([^()]*)\)|$)/g
  • 我认为我现在得到的几乎可以工作的正则表达式非常可读。 ([^\[]*)?(?:\[(.+?)\]\((.+?)\))? 基本上是一个匹配除左括号[ 之外的任何字符的组,如果可能的话,我会尝试匹配模式[+](+)。但我同意可读性是一种观点。这真的很私人。
  • @cbdev420 (?=.)(?!$) 是同义词。见this answer of mine
【解决方案3】:

这就是我最终使用的:

const text= 'textA [aaa](bbb) textB [ccc](ddd) textC'

const regexp = /(?!$)([^[]*)(?:\[(.*?)\]\((.*?)\))?/gm;

const array = Array.from(text.matchAll(regexp));

console.log(array);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-26
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    • 2013-12-25
    相关资源
    最近更新 更多