解析大文件时避免正则表达式回溯
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+ 字符,然后是 ]
。
【讨论】:
感谢您的回答!以上是关于解析大文件时避免正则表达式回溯的主要内容,如果未能解决你的问题,请参考以下文章
使用记事本,我可以在使用回溯时在正则表达式查找和替换期间删除空格吗?