解析大文件时避免正则表达式回溯

Posted

技术标签:

【中文标题】解析大文件时避免正则表达式回溯【英文标题】:Avoid regex backtracking while parsing big files 【发布时间】:2018-04-10 16:55:14 【问题描述】:

我正在解析一些符合模式的文件以生成人类可读的报告。我使用正则表达式来解析这些文件。

文件示例:

2012-05-10 08:00:00.155: BROADCAST - Body: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Data></Data>. MessageProperties [headers=X_Day=20120510]
2012-05-10 08:00:00.155: BROADCAST - Body: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Data></Data>. MessageProperties [headers=X_Day=20120510]
2012-05-10 08:00:00.155: REQUEST - Body: <?xml version="1.0" encoding="utf-8"?>
<Data xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <field1>field1.val</field1>
  <field2>field2.val</field2>
</Data>. MessageProperties [headers=X_Day=20120510, correlationId=[51, 56, 100, 54, 48, 48, 97, 54, 51, 99, 102, 100, 52, 102, 97, 51, 98, 51, 57, 52, 52, 49, 49, 50, 54, 97, 56, 100, 49, 48, 53, 98], other=blabla]

我要提取每条记录的时间部分、xml部分和属性部分。

正则表达式

目前我有这个正则表达式,它给了我我想要的东西(如果这有助于提高正则表达式的速度,我可以在以后的处理中提取所需的确切位) :

((?:[0-9]1,4[-| |:|\.])+[0-9]1,3): .*Body: ((?:.|>\n|>\r|>\r\n)*\. MessageProperties )(\[.*\])

文件可能很大(比如 2000-10000 个匹配项和 100Mb),所以我想对其进行一些优化。当前的问题是我在正文之前的 .* 和 MessageProperties 之前的 (?:.|>\n|>\r\n)* 的所有回溯(我需要明确包含换行符对于我给出的第三个示例记录)。

有没有办法优化所有这些回溯?我找不到办法。

我正在使用 regex101 进行开发,然后将其调整为 .Net

【问题讨论】:

老实说,如果您正在寻找性能,您可能想编写自己的解析代码而忘记正则表达式。 我不想那么难优化它,我认为这个正则表达式可以很容易地优化。目前我有一秒钟的时间来解析一个文件,但半秒钟左右我很好。 具体来说,您是否将该正则表达式应用于整个文件?如果是这样,我至少建议一次从文件中读取一行并将该行添加到缓冲区中。当您看到以日期开头的行时,处理缓冲区中的行,清除缓冲区并添加新行。 学习使用字符类。 (:|-|=)+ 应该写成[:=-]+ 感谢@juharr 的建议,如果没有人发布另一个答案,也许我会那样做。 【参考方案1】:

一般提示和改进

尽量避免单个字符交替,量化右边的部分而不是左边的部分,并尽可能使用字符类。两个字符串之间的未知文本最好使用unroll the loop principle 展开(也就是说,即使你很想这样做,也不要使用.*.*?)。

您的解决方案

你可以使用

^([0-9]4-[- :.0-9]*):\s+[^-]*\s+-\s+Body:\s+([^.]*(?:\.(?!\s+MessageProperties\s)[^.]*)*\.\s+MessageProperties\s+)(\[.*])

见regex demo

详情

^ - 行首(与RegexOptions.Multiline 选项一起使用,或者当(?m) 被添加到模式之前) ([0-9]4-[- :.0-9]*) - 第 1 组: [0-9]4 - 4 位数字 - - 一个连字符 [- :.0-9]* - 0+ 位、.:- 或空格字符 -:\s+[^-]*\s+-\s+ - :, 1+ 空格, 0+ - 以外的字符, 1+ 空格, -, 1+ 空格 Body: - 一个子字符串 \s+ - 1+ 个空格 ([^.]*(?:\.(?!\s+MessageProperties\s)[^.]*)*\.\s+MessageProperties\s+) - 第 2 组: [^.]*(?:\.(?!\s+MessageProperties\s)[^.]*)* - 展开的(?s:.*?):除. 之外的任何0+ 个字符,后跟. 的0+ 个序列,不跟MessageProperties,用1+ 个空格括起来,然后是除@987654348 之外的任何0+ 个字符@ \.\s+ - 一个 . 和 1+ 个空格 MessageProperties - 一个子字符串 \s+ - 1+ 个空格 (\[.*]) - 第 3 组:[ 后跟尽可能多的除换行符以外的任何 0+ 字符,然后是 ]

【讨论】:

感谢您的回答!

以上是关于解析大文件时避免正则表达式回溯的主要内容,如果未能解决你的问题,请参考以下文章

如何彻底避免正则表达式的灾难性回溯?

使用记事本,我可以在使用回溯时在正则表达式查找和替换期间删除空格吗?

正则表达式回溯法原理

使用正则表达式模式时的灾难性回溯错误

正则进阶之,回溯, (贪婪* 非贪婪+? 独占++)三种匹配量词

第977期正则表达式回溯法原理