RegEx 使用 RegExp.exec 从字符串中提取所有匹配项

IT技术 javascript regex regex-group taskwarrior
2021-01-23 17:53:48

我正在尝试解析以下类型的字符串:

[key:"val" key2:"val2"]

里面有任意键:“val”对。我想获取键名和值。对于那些好奇的人,我正在尝试解析任务战士的数据库格式。

这是我的测试字符串:

[description:"aoeu" uuid:"123sth"]

这是为了强调除了空格之外的任何东西都可以在键或值中,冒号周围没有空格,并且值总是在双引号中。

在节点中,这是我的输出:

[deuteronomy][gatlin][~]$ node
> var re = /^\[(?:(.+?):"(.+?)"\s*)+\]$/g
> re.exec('[description:"aoeu" uuid:"123sth"]');
[ '[description:"aoeu" uuid:"123sth"]',
  'uuid',
  '123sth',
  index: 0,
  input: '[description:"aoeu" uuid:"123sth"]' ]

而且description:"aoeu"也符合这个模式。我怎样才能取回所有比赛?

6个回答

继续re.exec(s)循环调用以获取所有匹配项:

var re = /\s*([^[:]+):\"([^"]+)"/g;
var s = '[description:"aoeu" uuid:"123sth"]';
var m;

do {
    m = re.exec(s);
    if (m) {
        console.log(m[1], m[2]);
    }
} while (m);

试试这个 JSFiddle:https ://jsfiddle.net/7yS2V/

在 Chrome 中执行此操作导致我的选项卡崩溃。
2021-03-20 17:53:48
@EdgeCaseBerg 您需要g设置标志,否则内部指针不会向前移动。文档
2021-03-22 17:53:48
使用 while 循环使得初始化 m 有点尴尬。您要么必须编写while(m = re.exec(s)),这是 IMO 的反模式,要么必须编写m = re.exec(s); while (m) { ... m = re.exec(s); }. 我更喜欢这个do ... if ... while成语,但其他技术也可以。
2021-03-25 17:53:48
另一点是,如果正则表达式可以匹配空字符串,它将是一个无限循环
2021-03-29 17:53:48
为什么不while代替do … while
2021-04-08 17:53:48

str.match(pattern), 如果pattern有 global flag g,则将所有匹配项作为数组返回。

例如:

const str = 'All of us except @Emran, @Raju and @Noman were there';
console.log(
  str.match(/@\w*/g)
);
// Will log ["@Emran", "@Raju", "@Noman"]

@AnthonyRoberts 您必须添加“g”标志。/@\w/g或者new RegExp("@\\w", "g")
2021-03-18 17:53:48
@madprog,对,这是最简单的方法,但在组值必不可少时不适合。
2021-03-27 17:53:48
注意:匹配不是匹配对象,而是匹配的字符串。例如,无法访问"All of us except @Emran:emran26, @Raju:raju13 and @Noman:noman42".match(/@(\w+):(\w+)/g)(将返回["@Emran:emran26", "@Raju:raju13", "@Noman:noman42"])中的组
2021-04-03 17:53:48
这对我不起作用。我只得到第一场比赛。
2021-04-07 17:53:48

要遍历所有匹配项,您可以使用以下replace函数:

var re = /\s*([^[:]+):\"([^"]+)"/g;
var s = '[description:"aoeu" uuid:"123sth"]';

s.replace(re, function(match, g1, g2) { console.log(g1, g2); });
@dudewad 抱歉,我没看到这里的懒惰部分。如果完全相同的方法被称为“处理”而不是“替换”,那么您就可以接受。恐怕您只是停留在术语上。
2021-03-10 17:53:48
这是违反直觉的代码。你没有在任何有意义的意义上“替换”任何东西。它只是将 some 函数用于不同的目的。
2021-03-13 17:53:48
@Christophe 我绝对不拘泥于术语。我被困在干净的代码上。出于某种原因将用于一个目的的东西用于不同的目的被称为“hacky”。它创建了难以理解的混乱代码,并且通常会在性能方面受到影响。您在没有正则表达式的情况下回答了这个问题这一事实本身就使它成为一个无效的答案,因为 OP 正在询问如何使用正则表达式来做到这一点。然而,我发现让这个社区保持高标准很重要,这就是为什么我坚持我上面所说的。
2021-03-20 17:53:48
@dudewad 如果工程师只是遵守规则而不跳出框框,我们现在甚至不会考虑访问其他行星;-)
2021-03-28 17:53:48
我认为这太复杂了。但是,很高兴知道做一件简单事情的不同方法(我赞成你的答案)。
2021-04-02 17:53:48

这是一个解决方案

var s = '[description:"aoeu" uuid:"123sth"]';

var re = /\s*([^[:]+):\"([^"]+)"/g;
var m;
while (m = re.exec(s)) {
  console.log(m[1], m[2]);
}

这是基于 lawnsea 的答案,但更短。

请注意,必须设置 `g' 标志以在调用之间向前移动内部指针。

str.match(/regex/g)

将所有匹配项作为数组返回。

如果出于某种神秘的原因,您需要附带的附加信息exec作为先前答案的替代,您可以使用递归函数而不是循环来完成,如下所示(这看起来也更酷:)。

function findMatches(regex, str, matches = []) {
   const res = regex.exec(str)
   res && matches.push(res) && findMatches(regex, str, matches)
   return matches
}

// Usage
const matches = findMatches(/regex/g, str)

正如之前的评论中所述,g在正则表达式定义的末尾在每次执行中将指针向前移动很重要

是的。递归看起来优雅和凉爽。迭代循环直截了当,更易于维护和调试。
2021-03-20 17:53:48
我喜欢递归解决方案,因为;我喜欢递归解决方案
2021-03-31 17:53:48