处理大文件时,使用正则表达式匹配出现了OutOfMemoryException异常。

3

我收到了一份来自生产代码发布的异常日志。

System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at System.Text.RegularExpressions.Match..ctor(Regex regex, Int32 capcount, String text, Int32 begpos, Int32 len, Int32 startpos)
   at System.Text.RegularExpressions.RegexRunner.InitMatch()
   at System.Text.RegularExpressions.RegexRunner.Scan(Regex regex, String text, Int32 textbeg, Int32 textend, Int32 textstart, Int32 prevlen, Boolean quick)
   at System.Text.RegularExpressions.Regex.Run(Boolean quick, Int32 prevlen, String input, Int32 beginning, Int32 length, Int32 startat)
   at System.Text.RegularExpressions.MatchCollection.GetMatch(Int32 i)
   at System.Text.RegularExpressions.MatchEnumerator.MoveNext()

需要处理的数据大约有800KB。

在我的本地测试中,它完美地工作。您是否见过类似的行为,可能是什么原因?

我应该在处理之前拆分文本,但显然在这种情况下,正则表达式可能无法匹配,因为原始文件从一个随机位置开始拆分。

我的正则表达式:

编辑2:

我认为这个特定的正则表达式导致了问题,在隔离环境中测试时,它会立即消耗内存。

((?:( |\.\.|\.|""|'|=)[\/|\?](?:[\w#!:\.\?\+=&@!$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})*)( |\.|\.\.|""|'| ))?
编辑 我在本地测试中出现了一些问题。我加载了一个大字符串,然后往里添加东西,这使得.NET Framework感到晕眩,并在RegEx期间而不是在字符串操作期间(或随机发生)引发OOM异常(忽略我之前说的内容)。 这是一个.NET Framework 2.0应用程序。

你能列出正在使用正则表达式的代码吗? - Seibar
3个回答

2

没有看到你的正则表达式,我不能确定,但有时候你会遇到这样的问题,因为你的匹配是贪婪的而不是懒惰的。

正则表达式引擎必须在内部存储大量信息,而贪婪匹配可能会导致正则表达式选择你800k字符串的大部分内容,多次重复选择。

关于这个问题有一些很好的信息可以在这里找到。


这是HTML,只需使用Yahoo的源代码,一切都需要很长时间。我现在尝试更改。 - dr. evil
似乎对我无效,那个正则表达式在我的测试程序中以30k的HTML文件为输入,会让CPU循环计数器到达极限! - Russ Clarke
它能够工作,但不再符合我的要求。尝试这个并看看区别 "/cool.htm dontmatch" 实际上,正则表达式本身就是垃圾,我相信有更好的方法来编写它。 - dr. evil
这个运行速度更快,但我不知道它是否准确: ( |..|.|""|'|=)/|?*( |.|..|""|'| ) - Russ Clarke
这个对我来说看起来更好,它不匹配链接类型为"text/css"的/css... (?:="?)(/|?*) - Russ Clarke
显示剩余4条评论

1

如果您的应用程序允许,我会尝试的第一件事是拆分输入。

如果输入是文件,是否可以逐行读取文件,并以这种方式应用正则表达式?

您应该使用CLR Profiler进行查看。学习如何使用可能需要一些时间,但它很值得。它将帮助您可视化对象使用了多少内存。


这有两个问题。首先,我最初在正则表达式中使用SingleLine作为选项。因此,逐行阅读可能会破坏某些正则表达式。其次,在较短的文件上,这将对性能产生不良影响(显然,我可以根据文件大小进行切换,但听起来很不好 :) 但是,如果我无法解决它,这是个好主意)。 - dr. evil
我敢打赌,对于小文件而言,逐行处理和一次性应用正则表达式之间的性能差异将是微不足道的,以至于不会产生明显的影响。 - Seibar
我知道stringbuilder在这方面很好,但我的意思是使用stringbuilder与正则表达式一起似乎可以帮助RegEx运行程序更有效地使用内存,这对我来说是全新的。 - dr. evil
这是可能的,我不确定StringBuilder是否能够更有效地使用Regex,但在我看来,这不是你应该依赖的东西。如果你的内存不足,那可能是一个更大的设计问题,需要解决。 - Seibar
显然我搞砸了我的本地测试,请查看我的最终编辑。 - dr. evil

1

根据您的编辑,听起来您的代码可能在创建占用大量内存的字符串。这意味着即使内存不足异常是从Regex代码中生成的,它实际上并不是因为Regex本身占用了太多内存。因此,如果在自己的代码中使用StringBuilder解决了问题,那么就应该这样做。


确实如你所说,我刚刚发现了这一点并更新了我的示例。不过这不是原始代码的行为,而是测试代码。请查看我的最终编辑。 - dr. evil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接