如何在JavaScript中检索正则表达式的所有匹配项?

gat*_*lin 156 javascript regex regex-group taskwarrior

我是正则表达式的新手.我正在尝试解析以下类型的字符串:

description:"aoeu"

哪里有任意键:里面有"val"对.我想抓住关键名称和价值.对于那些好奇的我正在尝试解析任务战士的数据库格式.这是我的测试字符串:

description:"aoeu"这是为了强调除了空格之外的任何东西都可以在键或值中,冒号周围没有空格,值总是用双引号.在节点中,这是我的输出:

[key:"val" key2:"val2"]
Run Code Online (Sandbox Code Playgroud)

description:"aoeu"也符合这种模式.我怎样才能把所有比赛都拿回来?

law*_*sea 211

继续调用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);
Run Code Online (Sandbox Code Playgroud)

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

  • @EdgeCaseBerg你需要设置`g`标志,否则内部指针不会向前移动.[文件](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec). (44认同)
  • 使用while循环使得初始化m有点尴尬.你要么写'while(m = re.exec(s))`,这是一个反模式的IMO,要么你必须写`m = re.exec(s); while(m){... m = re.exec(s); }`.我更喜欢`do ... if ... while`成语,但其他技巧也可以. (12认同)
  • 在铬中这样做会导致我的标签崩溃. (12认同)
  • 另一点是,如果正则表达式可以匹配空字符串,那么它将是一个无限循环 (10认同)
  • 为什么不`while`而不是`do ... while`? (6认同)
  • 另外,正则表达式应位于变量中,以使指针增加其位置。具有运行时正则表达式将导致无限循环。 (3认同)
  • @FabioCosta 刚刚遇到了无限循环的问题。令人沮丧的是,为了避免这个错误,每个人都应该手动检查当前和先前匹配的相等性,如果它们相同,则增加 RegExp 的 lastIndex 。答案肯定应该用这些信息进行更新。字符串的新“matchAll”方法可以自行执行此检查,这很好,现在我们可以完全拒绝使用循环(感谢@iuliu.net 提供的信息)。最后一个令人不快的点是 `RegExp` 包含状态,因此应该手动复制以避免出现不需要的突变。 (3认同)
  • @lawnsea 请将您的答案更新为:`let m; while (m = re.exec(s)) { console.log(m[1], m[2]); }` (2认同)

Ani*_*nis 100

str.match(pattern)将所有匹配作为数组返回.我想这是最简单的方法.

例如 -

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

  • 注意:匹配不是匹配对象,而是匹配的字符串.例如,除了@Emran之外,"我们所有人都无权访问":emran26,@ Raju:raju13和@Noman:noman42".match(/ @(\ w +):(\w +)/ g) (将返回`["@Emran:emran26","@ Raju:raju13","@ Noman:noman42"]`) (11认同)
  • @AnthonyRoberts你必须添加"g"标志.`/ @\w/g`或`new RegExp("@ \\ w","g")` (5认同)
  • @madprog,对,这是最简单的方法,但在组值必不可少时不适用. (4认同)
  • 这对我不起作用。我只得到第一场比赛。 (3认同)

Chr*_*phe 86

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

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

s.replace(re, function(match, g1, g2) { console.log(g1, g2); });
Run Code Online (Sandbox Code Playgroud)

  • 这是违反直觉的代码.你没有在任何有意义的意义上"替换"任何东西.它只是为了不同的目的利用某些功能. (20认同)
  • @dudewad如果工程师只是在遵循规则而没有跳槽思维,我们现在甚至都不会考虑拜访其他星球;-) (5认同)
  • @Christophe 我绝对不拘泥于术语。我被困在干净的代码上。出于某种原因将用于一个目的的东西用于不同的目的被称为“hacky”。它创建了难以理解的令人困惑的代码,并且通常会在性能方面受到影响。您在没有正则表达式的情况下回答了这个问题这一事实本身就使它成为一个无效的答案,因为 OP 正在询问如何使用正则表达式来做到这一点。然而,我发现让这个社区保持高标准很重要,这就是为什么我坚持我上面所说的。 (3认同)
  • @dudewad 抱歉,我看不到这里懒惰的部分。如果完全相同的方法被称为“处理”而不是“替换”,那么您就可以接受。恐怕您只是停留在术语上。 (2认同)

lov*_*soa 54

这是一个解决方案

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

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

这是基于lawsea的答案,但更短.

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


noe*_*ego 15

str.match(/regex/g)
Run Code Online (Sandbox Code Playgroud)

将所有匹配作为数组返回.

如果出于某种神秘的原因,你需要附加的信息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)
Run Code Online (Sandbox Code Playgroud)

如前面的注释中所述,重要的是g在正则表达式定义结束时在每次执行中向前移动指针.

  • 我喜欢递归解决方案,因为;我喜欢递归解决方案 (4认同)

Jef*_*kin 12

如果你有 ES9

(意味着如果您的系统:Chrome、Node.js、Firefox 等支持 Ecmascript 2019 或更高版本)

使用新的yourString.matchAll( /your-regex/ ).

如果你没有 ES9

如果您的系统较旧,这里有一个易于复制和粘贴的功能

function findAll(regexPattern, sourceString) {
    let output = []
    let match
    // make sure the pattern has the global flag
    let regexPatternWithGlobal = RegExp(regexPattern,[...new Set("g"+regexPattern.flags)].join(""))
    while (match = regexPatternWithGlobal.exec(sourceString)) {
        // get rid of the string copy
        delete match.input
        // store the match data
        output.push(match)
    } 
    return output
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

console.log(   findAll(/blah/g,'blah1 blah2')   ) 
Run Code Online (Sandbox Code Playgroud)

输出:

[ [ 'blah', index: 0 ], [ 'blah', index: 6 ] ]
Run Code Online (Sandbox Code Playgroud)


bob*_*bob 9

基于Agus的功能,但我更喜欢只返回匹配值:

var bob = "> bob <";
function matchAll(str, regex) {
    var res = [];
    var m;
    if (regex.global) {
        while (m = regex.exec(str)) {
            res.push(m[1]);
        }
    } else {
        if (m = regex.exec(str)) {
            res.push(m[1]);
        }
    }
    return res;
}
var Amatch = matchAll(bob, /(&.*?;)/g);
console.log(Amatch);  // yeilds: [>, <]
Run Code Online (Sandbox Code Playgroud)


sdg*_*sdh 7

可迭代项更好:

const matches = (text, pattern) => ({
  [Symbol.iterator]: function * () {
    const clone = new RegExp(pattern.source, pattern.flags);
    let match = null;
    do {
      match = clone.exec(text);
      if (match) {
        yield match;
      }
    } while (match);
  }
});
Run Code Online (Sandbox Code Playgroud)

循环使用:

for (const match of matches('abcdefabcdef', /ab/g)) {
  console.log(match);
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您想要一个数组:

[ ...matches('abcdefabcdef', /ab/g) ]
Run Code Online (Sandbox Code Playgroud)


Sim*_*ver 7

如果你能使用matchAll这里的一个技巧:

Array.From有一个“选择器”参数,因此您可以将其投影到您真正需要的内容,而不是最终得到一系列尴尬的“匹配”结果:

Array.from(str.matchAll(regexp), m => m[0]);
Run Code Online (Sandbox Code Playgroud)

如果您已命名组,例如。( /(?<firstname>[a-z][A-Z]+)/g) 你可以这样做:

Array.from(str.matchAll(regexp), m => m.groups.firstName);
Run Code Online (Sandbox Code Playgroud)

  • 现代方式! (2认同)

woo*_*666 6

我们终于开始看到一个内置matchAll函数,有关说明和兼容性表,请参见此处。截至2019年4月,似乎支持Chrome和Firefox,但不支持IE,Edge,Opera或Node.js. 好像它是在2018年12月起草的,所以给它一些时间来访问所有浏览器,但我相信它会到达那里。

内置matchAll函数很不错,因为它返回了iterable。它还会为每次比赛返回捕获组!所以你可以做类似的事情

// get the letters before and after "o"
let matches = "stackoverflow".matchAll(/(\w)o(\w)/g);

for (match of matches) {
    console.log("letter before:" + match[1]);
    console.log("letter after:" + match[2]);
}

arrayOfAllMatches = [...matches]; // you can also turn the iterable into an array
Run Code Online (Sandbox Code Playgroud)

似乎每个匹配对象都使用与相同的格式match()。因此,每个对象是匹配和捕获组的阵列,用另外的三个属性沿indexinputgroups。所以看起来像:

[<match>, <group1>, <group2>, ..., index: <match offset>, input: <original string>, groups: <named capture groups>]
Run Code Online (Sandbox Code Playgroud)

有关更多信息,matchAll还有一个Google开发人员页面。也有可用的填充/垫片


Agu*_*tra 5

这是我获得比赛的功能:

function getAllMatches(regex, text) {
    if (regex.constructor !== RegExp) {
        throw new Error('not RegExp');
    }

    var res = [];
    var match = null;

    if (regex.global) {
        while (match = regex.exec(text)) {
            res.push(match);
        }
    }
    else {
        if (match = regex.exec(text)) {
            res.push(match);
        }
    }

    return res;
}

var regex = /abc|def|ghi/g;
var res = getAllMatches(regex, 'abcdefghi');

res.forEach(function (item) {
    console.log(item[0]);
});
Run Code Online (Sandbox Code Playgroud)