【问题标题】:Is my simple regex catastrophically backtracking?我的简单正则表达式是否灾难性地回溯?
【发布时间】:2020-12-23 23:54:01
【问题描述】:

我正在使用解析文本文件

(?<DateTime>.+?\t.+?)\t(?<Data>.+?)(\t(?<Data2>.+?))?\r\n

原来只是

(?<DateTime>.+?\t.+?)\t(?<Data>.+?)\r\n

但后来我发现一个文件有一个额外的列,需要在这个 API 应该解析的 10 个文件中加以说明,所以我必须对其进行编辑以提出第一个正则表达式。

这是我正在解析的数据的示例

2020-05-26  08:30:06    18.6
2020-05-26  08:44:38    18.0
2020-05-26  08:52:04    17.5
2020-05-26  09:17:44    18.0
2020-05-26  10:25:35    17.5
2020-05-26  10:47:08    18.0
2020-05-26  11:06:08    18.5

这里是流氓列的数据

2019-08-21  10:32:21    0   00000   
2019-08-21  19:21:37    0   00000   
2019-08-21  23:24:10    0   00000   
2019-08-22  00:47:39    0   00000   

请注意,虽然现在这些都是零,但其他值也是可能的

现在这里的一切仍在“工作”,但是在我对正则表达式进行编辑之后,现在有一个大约 8000 条记录的文件需要很长时间才能处理。我在 parse 方法中编写了一些控制台输出,发现它似乎在行 ~7700 附近停止了将近 10 分钟,然后突然以 500 退出。这是我的 parse 方法(我认为这不重要,但我正在抛出反正这个)

DataRow row;
index = 0;
Console.WriteLine("Beginning parse loop");
foreach (Match match in reg.Matches(data)) {
    row = table.NewRow();
    foreach (List<string> column in columns) {
        string value = getRegexGroupValue(match, column);
        if (column[1] == "System.DateTime") {
           if (value != "") {
              row[column[0]] = Convert.ToDateTime(value);
           }
        } else if (column[1] == "System.Int32") {
            row[column[0]] = Convert.ToInt32(value);
        } else {
            row[column[0]] = value;
        }
    }

    table.Rows.Add(row);
    Console.WriteLine(String.Format("Ending loop {0}", index++));
}

这是怎么回事?

当我在调试控制台中使用 reg.Matches(data).Count 时,它会说一些错误并且没有显示行数,但是当我使用 Notepad++ 检查正则表达式时,我可以得到行总数就好了

编辑:我再次使用(?&lt;DateTime&gt;.+?\t.+?)\t(?&lt;Data&gt;.+?)[(\t)(\r\n)] 处理文件,但这不是最好的解决方案,因为我不再捕获该文件中的额外列,不确定我们是否会使用它,但我宁愿拥有比没有

【问题讨论】:

  • 你输了?: (&lt;Data2&gt;.+?) => (?&lt;Data2&gt;.+?)
  • 似乎在空白处分割会更容易。
  • 很好,我已经创建了一个解决方法正则表达式并且不得不重写原来的,看起来我放弃了我的?
  • 正如@ggorlen 所说,仅在空白处(在本例中为\t)分割并处理各个元素会容易得多。你真的有充分的理由使用正则表达式吗?
  • 我不知道这算不算一个“非常好的”理由,但是使用命名的捕获组让我可以非常灵活地处理其余代码,此外还有一种简化、标准化的方式解析文本文件(这只是 15+ ETL API 中的一小部分)。我的另一个选择是遍历输入文件中的每一行并编写代码来手动解析每种类型的文件,我想不出一种简单而标准的方法。这支持我下一步将所有这些移植到所有 API 都将使用的共享“ETL”类中,而不是将所有代码复制粘贴到其中

标签: c# regex .net-core


【解决方案1】:

你使用.+? 太多了。使用否定字符类并使用锚点:

(?m)^(?<DateTime>[^\t\r\n]+\t[^\t\r\n]+)\t(?<Data>[^\t\r\n]+)(?:\t(?<Data2>[^\t\r\n]+))?\r?$

proof

说明

                           EXPLANATION
--------------------------------------------------------------------------------
  (?m)                     set flags for this block (with ^ and $
                           matching start and end of line) (case-
                           sensitive) (with . not matching \n)
                           (matching whitespace and # normally)
--------------------------------------------------------------------------------
  ^                        the beginning of a "line"
--------------------------------------------------------------------------------
  (?<DateTime>             group and capture to \k<DateTime>:
--------------------------------------------------------------------------------
    [^\t\r\n]+               any character except: '\t' (tab), '\r'
                             (carriage return), '\n' (newline) (1 or
                             more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
    \t                       '\t' (tab)
--------------------------------------------------------------------------------
    [^\t\r\n]+               any character except: '\t' (tab), '\r'
                             (carriage return), '\n' (newline) (1 or
                             more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
  )                        end of \k<DateTime>
--------------------------------------------------------------------------------
  \t                       '\t' (tab)
--------------------------------------------------------------------------------
  (?<Data>                  group and capture to \k<Data>:
--------------------------------------------------------------------------------
    [^\t\r\n]+               any character except: '\t' (tab), '\r'
                             (carriage return), '\n' (newline) (1 or
                             more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
  )                        end of \k<Data>
--------------------------------------------------------------------------------
  (?:                      group, but do not capture (optional
                           (matching the most amount possible)):
--------------------------------------------------------------------------------
    \t                       '\t' (tab)
--------------------------------------------------------------------------------
    (?<Data2>              group and capture to \k<Data2>:
--------------------------------------------------------------------------------
      [^\t\r\n]+               any character except: '\t' (tab), '\r'
                               (carriage return), '\n' (newline) (1
                               or more times (matching the most
                               amount possible))
--------------------------------------------------------------------------------
    )                        end of \k<Data2>
--------------------------------------------------------------------------------
  )?                       end of grouping
--------------------------------------------------------------------------------
  \r?                      '\r' (carriage return) (optional (matching
                           the most amount possible))
--------------------------------------------------------------------------------
  $                        before an optional \n, and the end of a
                           "line"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    • 1970-01-01
    • 2016-03-24
    • 2017-09-24
    • 1970-01-01
    • 1970-01-01
    • 2016-10-07
    相关资源
    最近更新 更多