如何只保留第一个独特的正则表达式匹配?

时间:2018-02-14 00:37:20

标签: javascript regex

让我们从我拥有的东西开始。 我正在使用一个从许多节点收集日志的系统。 短消息显示在网页上,其中包含指向特定节点的更多信息的链接。 可以看到几百到几千行的任何地方。

我在底部创建了一个可以运行的大量缩减示例。

条款解释: 节点id =例如“c01m01”这表示一个节点,“c01m02”,“c02m01”将是另外两个节点。 短信示例:“错误c01m01无法启动”或“警告c03m01无法解析业务报告”

我想要的是创建一个只保留第一个唯一匹配的正则表达式,在这种特殊情况下通过节点识别。

我对包含“业务”或“管理”的内容不感兴趣,因为它还没有正常工作。

所以我希望在匹配中删除包含这些单词的消息。 完成后,我希望每个唯一节点引用的最顶行作为匹配项,但不是列表下方的那个节点的任何内容。

至于我的例子,粗体节点是想要无差别匹配的:

  

详细 c01m01 启动确定

     

警告 c02m02 无法解析业务报告

     

详细c01m01开始

     

警告 c02m02 错过配置

     

详细c02m02启动确定

     

详细说明c02m02开始

     

详细 c03m05 开始

请注意第二行上的 c02m02 ,我希望匹配,即使它是来自该节点的最顶层消息。

如果有帮助的话,愿意使用带有后备支持的 es2018 。 如果尚未启用,则可以在大多数基于Chrome的chrome://flags/#enable-javascript-harmony浏览器中启用它,尽管它仍然是实验性的,但尚未被认为是稳定的。

我尝试过诸如/(?<!\A[\S\s]*?\1[\S\s]*?\G)( c\d{2}m\d{2} )(?!.*(business|management))/ig之类的表达式以及其中的变种,但没有取得任何成功。不确定\A\G是否可以在这种正则表达式中使用,但我已经尝试了一大堆其他表达式,不包括其中任何一种。 我最接近的是匹配每个节点的最后一个条目(靠近我想要的相反)与/( c\d{2}m\d{2} )(?![\s\S]*\1)/ig

我是正则表达式的初学者,可能会遗漏一些微不足道的东西,但到目前为止,我对此事的互联网研究还没有出现任何问题。 我想要实现的只是不可能使用正则表达式,特别是js-regex?

非常感谢任何有关此事的帮助,并且更详细的解释。

function regex_mod_fn() {
  let msgRows = document.getElementsByName('entry');

  function show_all() {
    for (let i = 0; i < msgRows.length; i++) {
      msgRows[i].style.display = document.defaultRowDisplay;
    }
  }

  // Validation needs more work.
  let validInput = /^\/.+\/\S*$/;
  let re_input = document.getElementById('regex_input');
  let re_raw = re_input.value;
  if (!validInput.test(re_raw)) {
    re_input.style.borderColor = !!(re_raw.length) ? 'red' : '';
    show_all();
    return;
  }
  re_input.style.borderColor = '';

  // Construct the regex object
  let re_str = re_raw.substr(1, re_raw.lastIndexOf('/') - 1);
  let re_flags = re_raw.substr(re_raw.lastIndexOf('/') + 1);
  let re = undefined;
  try {
    if (re_flags === '')
      re = new RegExp(re_str);
    else
      re = new RegExp(re_str, re_flags);
  } catch (e) {
    re_input.style.borderColor = 'red';
    console.error(e);
    return;
  }

  const msgList = document.msgList;

  let result = null;

  // Save the number of rows and character position for each new line.
  let newLineIndex = [];
  let reNewLine = /\n/g;
  while ((result = reNewLine.exec(msgList)) !== null) {
    newLineIndex.push(reNewLine.lastIndex);
  }

  // Find the matches and save the row index
  let matchRow = [];
  while ((result = re.exec(msgList)) !== null) {
    for (let i = 0; i < newLineIndex.length; i++) {
      if (result.index < newLineIndex[i]) {
        matchRow.push(i);
        break;
      }
    }

    if (!re.global) {
      break;
    }
  }

  // Sort and remove duplicates
  matchRow = matchRow.sort(function(a, b) {
    return a - b;
  }).filter((x, i, a) => !i || x != a[i - 1]);

  // Show/hide rows, based on if they match the regex
  let idx = 0;
  for (let i = 0; i < msgRows.length; i++) {
    let row = msgRows[i];
    if (matchRow[idx] === i) {
      row.style.display = document.defaultRowDisplay;
      idx++;
    } else {
      row.style.display = 'none';
    }
  }
}

function init() {
  document.defaultRowDisplay = document.getElementsByName('entry')[0].style.display;
  document.msgList = '';

  let msgRows = document.getElementsByName('entry');
  for (let i = 0; i < msgRows.length; i++) {
    let msg = msgRows[i].firstElementChild.innerHTML;
    document.msgList += msg + '\n';
  }
  regex_input.addEventListener('input', regex_mod_fn);
}

init();
<input id="regex_input" type="text" size=50 placeholder="regex" />
<table>
  <thead>
    <tr>
      <th>Message</th>
      <th>Link</th>
      <th>Log Size</th>
    </tr>
  </thead>
  <tbody>
    <tr name="entry">
      <td>verbose c01m01 startup ok</td>
      <td><a href="http://www.example.com">details</a></td>
      <td>1.14 KB</td>
    </tr>
    <tr name="entry">
      <td>warn c02m02 unable to parse business report</td>
      <td><a href="http://www.example.com">details</a></td>
      <td>4.30 MB</td>
    </tr>
    <tr name="entry">
      <td>verbose c01m01 starting</td>
      <td><a href="http://www.example.com">details</a></td>
      <td>753.78 KB</td>
    </tr>
    <tr name="entry">
      <td>warn c02m02 miss-configured</td>
      <td><a href="http://www.example.com">details</a></td>
      <td>1.14 KB</td>
    </tr>
    <tr name="entry">
      <td>verbose c02m02 startup ok</td>
      <td><a href="http://www.example.com">details</a></td>
      <td>4.30 MB</td>
    </tr>
    <tr name="entry">
      <td>verbose c02m02 starting</td>
      <td><a href="http://www.example.com">details</a></td>
      <td>753.78 KB</td>
    </tr>
    <tr name="entry">
      <td>verbose c03m05 starting</td>
      <td><a href="http://www.example.com">details</a></td>
      <td>1.14 KB</td>
    </tr>
  </tbody>
</table>

1 个答案:

答案 0 :(得分:1)

我认为这就是你要找的东西。诀窍是建立先前匹配的节点列表。你的起始正则表达式非常接近,你唯一缺少的部分就是尝试使用正则表达式完全解决它 。每次基于先前的匹配创建新的正则表达式模式将使您第一次出现每个节点(除了包含业务管理的行之外,如您所请求的那样)

const str = `
verbose c01m01 startup ok

warn c02m02 unable to parse business report

verbose c01m01 starting

warn c02m02 miss-configured

verbose c02m02 startup ok

verbose c02m02 starting

verbose c03m05 starting
`;

const previousNodes = [];
const nodeRegexString = '(c\\d{2}m\\d{2})';
const exclusions = ['business', 'management'];

const returnUniqueNodes = str =>
    str.split('\n').filter(line => {
        const regex = new RegExp(
            `.*${previousNodes.length
                ? `(?!.*${previousNodes.join('|')})`
                : ''}${nodeRegexString}${exclusions.length
                ? `(?!.*${exclusions.join('.*|')})`
                : ''}.*`,
            'im'
        );

        if (regex.test(line)) {
            previousNodes.push(line.match(new RegExp(nodeRegexString))[0]);
            return line;
        } else {
            return false;
        }
    });

console.log(returnUniqueNodes(str));

https://repl.it/@ryanpcmcquen/returnUniqueNodes