我试图解析以下类型的字符串:

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

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

这是我的测试字符串:

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

这意味着除了空格之外,任何东西都可以放在键或值中,冒号周围没有空格,值总是在双引号中。

在node中,这是我的输出:

[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"]' ]

但是描述:“aoeu”也符合这个模式。我怎么能得到所有的比赛回来?


当前回答

用这个……

var all_matches = your_string.match(re);
console.log(all_matches)

它将返回一个包含所有匹配项的数组…这很好.... 但是记住它不会考虑分组,它只会返回完整的匹配。

其他回答

可迭代对象更好:

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);
  }
});

循环中的用法:

for (const match of matches('abcdefabcdef', /ab/g)) {
  console.log(match);
}

或者如果你想要一个数组:

[ ...matches('abcdefabcdef', /ab/g) ]

这里有一个没有while循环的一行解决方案。

结果列表中保留该顺序。

潜在的缺点是

它为每个匹配复制正则表达式。 结果与预期的解形式不同。你需要再处理一次。

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

(str.match(re) || []).map(e => RegExp(re.source, re.flags).exec(e))

[ [ 'description:"aoeu"',
    'description',
    'aoeu',
    index: 0,
    input: 'description:"aoeu"',
    groups: undefined ],
  [ ' uuid:"123sth"',
    'uuid',
    '123sth',
    index: 0,
    input: ' uuid:"123sth"',
    groups: undefined ] ]

如果你有ES9

(意思是如果你的系统:Chrome, Node.js, Firefox等支持Ecmascript 2019或更高版本) 使用新的yourString。matchAll(/your-regex/g)。

如果你没有ES9

如果您使用的是较旧的系统,这里有一个简单的复制和粘贴函数

function findAll(regexPattern, sourceString) {
    let output = []
    let match
    // auto-add global flag while keeping others as-is
    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
}

使用示例:

console.log(   findAll(/blah/g,'blah1 blah2')   ) 

输出:

[ [ 'blah', index: 0 ], [ 'blah', index: 6 ] ]

这并不能真正帮助你解决更复杂的问题,但我还是发布了这篇文章,因为对于那些不像你这样做全局搜索的人来说,这是一个简单的解决方案。

我简化了答案中的正则表达式,以使其更清晰(这不是您的确切问题的解决方案)。

var re = /^(.+?):"(.+)"$/
var regExResult = re.exec('description:"aoeu"');
var purifiedResult = purify_regex(regExResult);

// We only want the group matches in the array
function purify_regex(reResult){

  // Removes the Regex specific values and clones the array to prevent mutation
  let purifiedArray = [...reResult];

  // Removes the full match value at position 0
  purifiedArray.shift();

  // Returns a pure array without mutating the original regex result
  return purifiedArray;
}

// purifiedResult= ["description", "aoeu"]

这看起来比没有注释时更冗长,这是没有注释时的样子

var re = /^(.+?):"(.+)"$/
var regExResult = re.exec('description:"aoeu"');
var purifiedResult = purify_regex(regExResult);

function purify_regex(reResult){
  let purifiedArray = [...reResult];
  purifiedArray.shift();
  return purifiedArray;
}

注意,任何不匹配的组都将作为未定义的值列在数组中。

该解决方案使用ES6展开操作符来净化正则表达式特定值的数组。如果你想要IE11支持,你需要通过Babel来运行你的代码。

我们终于开始看到一个内置的matchAll函数,请参阅这里的描述和兼容性表。似乎到2020年5月,Chrome、Edge、Firefox和Node.js(12+)都被支持,但IE、Safari和Opera不支持。它似乎是在2018年12月起草的,所以给它一些时间来传播到所有浏览器,但我相信它会到达那里。

内置的matchAll函数很好,因为它返回一个可迭代对象。它还为每个匹配返回捕获组!你可以这样做

// 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

似乎每个匹配对象都使用与match()相同的格式。因此,每个对象都是匹配和捕获组的数组,以及三个附加属性索引、输入和组。它看起来是这样的:

[<match>, <group1>, <group2>, ..., index: <match offset>, input: <original string>, groups: <named capture groups>]

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