实现大文本文件阅读器的最佳策略

我们有一个应用程序将其处理步骤记录到文本文件中。 在实施和测试期间使用这些文件来分析问题。 每个文件的大小最多为10MB,最多包含100,000个文本行。

目前,通过打开文本查看器(Notepad ++ etc)并根据问题查找特定字符串和数据来完成对这些日志的分析。

我正在构建一个有助于分析的应用程序。 它将使用户能够读取文件,搜索,突出显示特定字符​​串以及与隔离相关文本相关的其他特定操作。

这些文件不会被编辑!

在玩一些概念时,我立即发现TextBox(或RichTextBox)不能很好地处理大文本的显示。 我设法使用具有可接受性能的DataGridView实现查看器,但该控件不支持特定字符串的颜色突出显示。

我现在想将整个文本文件作为字符串保存在内存中,并且只在RichTextBox中显示非常有限数量的记录。 对于滚动和导航,我想添加一个独立的滚动条。

我对这种方法的一个问题是如何从存储的字符串中获取特定的行。

如果有人有任何想法,可以突出我的方法的问题,那么谢谢你。

我建议将整个内容加载到内存中,但是作为字符串的集合而不是单个字符串。 这很容易做到:

string[] lines = File.ReadAllLines("file.txt"); 

然后,您可以使用LINQ搜索匹配的行,轻松显示它们等。

这是一种可以在具有多个内核的现代CPU上很好地扩展的方法。

您创建一个迭代器块,用于生成文本文件中的行(如果需要,还可以生成多个文本文件):

 IEnumerable GetLines(String fileName) { using (var streamReader = File.OpenText(fileName)) while (!streamReader.EndOfStream) yield return streamReader.ReadLine(); } 

然后使用PLINQ并行搜索线条。 如果你有一个现代的CPU,这样做可以大大加快搜索速度。

 GetLines(fileName) .AsParallel() .AsOrdered() .Where(line => ...) .ForAll(line => ...); 

您在Where中匹配您需要提取的行提供谓词。 然后,您向ForAll提供一个动作,将行发送到最终目的地。

这是您需要做的简化版本。 您的应用程序是GUI应用程序,您无法在主线程上执行搜索。 您必须为此启动后台任务。 如果您希望此任务可以取消,则需要在GetLines方法中检查while循环中的取消标记。

ForAll将在线程池中调用线程上的操作。 如果要将匹配的行添加到用户界面控件,则需要确保在用户界面线程上更新此控件。 根据您使用的UI框架,有不同的方法可以做到这一点。

此解决方案假定您可以通过执行文件的单个正向传递来提取所需的行。 如果您需要根据用户输入进行多次传递,则可能需要将文件中的所有行缓存到内存中。 缓存10 MB并不多,但可以说你决定搜索多个文件。 缓存1 GB甚至可以使function强大的计算机变得紧张,但使用更少的内存和更多的CPU,因为我建议您可以在现代台式PC上的合理时间内搜索非常大的文件。

我想当一个人拥有多GB的RAM时,一个人自然会倾向于“将整个文件加载到内存中”的路径,但是这里的任何人都对这个问题的浅层理解感到满意吗? 当这个人想要加载一个4千兆字节的文件时会发生什么? (是的,可能不太可能,但编程往往是关于抽象的扩展,并且将整个内容加载到内存中的快速修复是不可扩展的。)

当然,存在相互竞争的压力:您是否需要昨天的解决方案,或者您是否有足够的时间深入研究问题并学习新的东西? 该框架还通过将块模式文件呈现为流来影响您的思考…您必须检查流的BaseStream.CanSeek值,如果是,则访问BaseStream.Seek()方法以获得随机访问。 不要误会我的意思,我绝对喜欢.NET框架,但我看到一个建筑工地,一群“木匠”不能把房子放在框架上,因为空气压缩机坏了而且他们没有知道如何使用锤子。 上蜡,下蜡,教人钓鱼等。

所以,如果你有时间,请查看滑动窗口。 您可以通过使用内存映射文件(让框架/操作系统管理滑动窗口)以简单的方式执行此操作,但有趣的解决方案是自己编写。 基本的想法是,您在任何时候只有一小部分文件被加载到内存中(文件的一部分在您的界面中可见,两侧可能有一个小缓冲区)。 当您向前移动文件时,可以保存每行开头的偏移量,以便您可以轻松搜索文件的任何早期部分。

是的,有性能影响……欢迎来到现实世界,其中一个人面临各种要求和约束,并且必须在时间和内存利用率之间找到可接受的平衡。 这是编程的乐趣……找出可以达到目标的各种方式,并了解各种路径之间的权衡。 这就是你超越办公室里那个人的技能水平的过程,他们把每个问题看作钉子,因为他只知道如何使用锤子。

[/咆哮]

我建议在.NET 4中使用MemoryMappedFile (或通过以前版本中的DllImport)来处理屏幕上可见的文件的一小部分,而不是浪费内存和加载整个文件的时间。