Regex来查找特定的键值对

qco9c6ql  于 2023-06-25  发布在  其他
关注(0)|答案(3)|浏览(140)

我有一个字符串列表

time:1001 name:foo avg:5.7
time:1002 
time:1003 avg:1.2
time:1004 name:f 
time:1005 name:bar avg:2.1

我想挑出与time:value name:value avg:value完全匹配的字符串并提取

[time, 1001], [name, foo], [avg, 5.7]
[time, 1005], [name, bar], [avg, 2.1]

使用regex:/(\w+):\s*(?:"([^"]*)"|(\S+))/g
代码:

const regex = /(\w+):\s*(?:"([^"]*)"|(\S+))/g;
let match = regex.exec(line);

我知道所有的台词,

[time, 1001], [name, foo], [avg, 5.7]
[time:1002], [name:undefined], [avg, undefined]
[time:1003], [name:undefined], [avg:1.2]
[time:1004], [name:f], [avg, undefined]
[time, 1005], [name, bar], [avg, 2.1]

如何选择所有键值都存在的那些

a11xaf1n

a11xaf1n1#

一种可能的方法,它完全消耗数据blob,就像…

time:1001 name:foo avg:5.7
time:1002 
time:1003 avg:1.2
time:1004 name:f 
time:1005 name:bar avg:2.1

...可以基于全局标记的regex like...

/time\s*\:\s*(?<time>\S+)\s*name\s*\:\s*(?<name>\S+)\s*avg\s*\:\s*(?<avg>\S+)/g

...它使用named capturing groups并通过matchAll应用。
由于生成的迭代器的数组表示仅具有有效匹配,因此可以map所述数组,其中对于每个迭代步骤,确实通过例如a template literal.然后,Map的数组只需要用newline/'\n'进行join
接下来提供的可执行示例代码生成的结果正是OP所要求的,从而证明了上面所说的/解释的所有内容......

const sampleData =
`time:1001 name:foo avg:5.7
time:1002 
time:1003 avg:1.2
time:1004 name:f 
time:1005 name:bar avg:2.1`;

const regXCapture =
  // see ... [https://regex101.com/r/hoiUFH/1]
  /time\s*\:\s*(?<time>\S+)\s*name\s*\:\s*(?<name>\S+)\s*avg\s*\:\s*(?<avg>\S+)/g;

console.log(
  [...sampleData.matchAll(regXCapture)]
    .map(({ groups: { time, name, avg } }) =>
      `[time, ${ time }], [name, ${ name }], [avg, ${ avg }]`
    )
    .join('\n')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
    • 摘要**

与OP的正则表达式不同,它通常捕获每个键值对,同时不知道键,上面的正则表达式非常明确地指出了应该匹配什么。它不仅声明每个键的名称,还要求键值对匹配的特定序列。
这就是OP的正则表达式不能全局操作多行字符串的原因。虽然它可以用于单行,但OP似乎没有正确处理匹配结果...但是OP没有提供这部分代码。
下一个示例代码演示了OP的正则表达式的可能用法,它精确地创建了OP的预期结果。然而,regex和下面的实现都不适合任何不是当前使用的多行数据形式的数据,因为人们从不检查匹配的键名的有效性。

const sampleData =
`time: "1001"  name:  foo avg: 5.7
time:1002 
time:1003 avg:1.2
time:1004 name:f 
time:1005  name:"bar"  avg:   2.1`;

const regXKeyValue =
  /(\w+):\s*(?:"([^"]*)"|(\S+))/g;

console.log(
  sampleData

    .split(/\n/)
    .reduce((collector, line) => {

      const matches = [...line.matchAll(regXKeyValue)];

      if (matches.length === 3) {
        collector
          .push(
            matches
              .map(([match, key, dequotedValue, unquotedValue]) =>
                `[${ key }, ${ dequotedValue ?? unquotedValue }]`
              )
              .join(', ')
          );
      }
      return collector;
    
    }, []).join('\n')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
ds97pgxw

ds97pgxw2#

让你的regexp与键完全匹配,而不是使用\w+来匹配任何东西。

const lines = [
  'time:1001 name:foo avg:5.7 ',
  'time:1002 ',
  'time:1003 avg:1.2 ',
  'time:1004 name:f  ',
  'time:1005 name:bar avg:2.1 ',
];

const regex = /time:(\d+)\s+name:(\w+)\s+avg:([\d.]+)/;

result = [];
lines.forEach(line => {
  m = line.match(regex);
  if (m) {
    result.push([
      ['time', m[1]],
      ['name', m[2]],
      ['avg', m[3]]
    ]);
  }
});

console.log(result);
wqlqzqxt

wqlqzqxt3#

这个问题被编辑了好几次,误导了大家。最后一次编辑在regexp中包含了双引号。所以基本上我不再关心了。这里只列出2种方法,并对它们进行基准测试,以供我将来参考:
您可以动态创建regexp以使用捕获组:

const str = `time:1001 name:foo avg:5.7
time:1002 
time:1003 avg:1.2
time:1004 name:f 
time:1005 name:bar avg:2.1`;

const result = [];

const regex = new RegExp('(\\w+):([^\\s]+)(?:[^\\n]|$)'.repeat(3), 'g');
let m;
while (m = regex.exec(str)) {
    result.push(m.slice(1).reduce(
        (arr, item, idx) => (arr[Math.floor(idx / 2)] ??=[]).push(item) && arr
        , []
    ));
}

console.log(JSON.stringify(result));

或者使用{3}来告诉你一行中只需要3个键:值对,然后手动解析一行:

const str = `time:1001 name:foo avg:5.7
time:1002 
time:1003 avg:1.2
time:1004 name:f 
time:1005 name:bar avg:2.1`;

const result = str.match(/(\w+:[^\s]+[^\n]*){3}/g).map(line => 
  line.split(/\s+/g).map(item => item.split(':'))
);

console.log(JSON.stringify(result));

正如我所期望的那样,动态regexp的速度明显更快,因为它已经包含了捕获的令牌:

<script benchmark data-count="1">

const str = `time:1001 name:foo avg:5.7
    time:1002 
    time:1003 avg:1.2
    time:1004 name:f 
    time:1005 name:bar avg:2.1
    `.repeat(100000);

    // @benchmark dynamic regexp

    const result = [];

    const regex = new RegExp('(\\w+):([^\\s]+)(?:[^\\n]|$)'.repeat(3), 'g');
    let m;
    while (m = regex.exec(str)) {
        result.push(m.slice(1).reduce(
            (arr, item, idx) => (arr[Math.floor(idx / 2)] ??=[]).push(item) && arr
            , []
        ));
    }

    result;

    // @benchmark repeated capture groups

    str.match(/(\w+:[^\s]+[^\n]*){3}/g).map(line => 
      line.split(/\s+/g).map(item => item.split(':'))
    );    

</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>

相关问题