我是've got the lovely task of working out how to handle large files being loaded into our application'的脚本编辑器(对于我们用于快速宏的内部产品,它就像VBA) . 大多数文件大约300-400 KB,这是很好的加载 . 但是当它们超过100 MB时,这个过程很难(正如你所期望的那样) .
发生的事情是将文件读取并推入RichTextBox然后导航 - 不要过于担心这部分 .
编写初始代码的开发人员只是使用StreamReader并且正在执行
[Reader].ReadToEnd()
这可能需要很长时间才能完成 .
我的任务是打破这段代码,将其以块的形式读入缓冲区并显示一个带有取消选项的进度条 .
一些假设:
-
大多数文件将是30-40 MB
-
文件的内容是文本(非二进制),有些是Unix格式,有些是DOS .
-
检索完内容后,我们计算出使用的终结符 .
-
没有人's concerned once it'加载了在richtextbox中渲染所需的时间 . 这只是文本的初始加载 .
现在提问:
-
我可以简单地使用StreamReader,然后检查Length属性(so ProgressMax)并发出Read for set buffer size并在后台worker中的while循环WHILST中迭代,所以它没有完成.2602446_ .
-
内容将转到StringBuilder . 如果长度可用,我可以用流的大小初始化StringBuilder吗?
这些(在您的专业意见中)是好主意吗?我过去曾经有一些问题从Streams读取内容,因为它总会错过最后几个字节或者其他东西,但如果是这样的话,我会问另一个问题 .
11 回答
您可以使用BufferedStream提高读取速度,如下所示:
March 2013 UPDATE
我最近编写了用于读取和处理(搜索文本)1 GB-ish文本文件的代码(比这里涉及的文件大得多),并通过使用 生产环境 者/消费者模式实现了显着的性能提升 . 生产环境 者任务使用
BufferedStream
以文本行读取,并将它们交给执行搜索的单独的消费者任务 .我用它来学习TPL数据流,这非常适合快速编码这种模式 .
Why BufferedStream is faster
December 2014 UPDATE: Your Mileage May Vary
根据注释,FileStream应该在内部使用BufferedStream . 在首次提供此答案时,我通过添加BufferedStream测量了显着的性能提升 . 当时我在32位平台上瞄准.NET 3.x.今天,在64位平台上面向.NET 4.5,我没有看到任何改进 .
Related
我遇到了一个案例,从ASP.Net MVC操作将大量生成的CSV文件传输到Response流非常慢 . 在这种情况下,添加BufferedStream可将性能提高100倍 . 欲了解更多,请参阅Unbuffered Output Very Slow
您说在加载大文件时,系统会要求您显示进度条 . 这是因为用户真的想要查看文件加载的确切百分比,还是因为他们想要视觉反馈才能发生某些事情?
如果后者是真的,则解决方案变得更加简单 . 只需在后台线程上执行
reader.ReadToEnd()
,并显示选取框类型的进度条而不是正确的进度条 .我提出这一点,因为根据我的经验,这种情况经常发生 . 当您编写数据处理程序时,用户肯定会对%完整数字感兴趣,但对于简单但缓慢的UI更新,他们更可能只想知道计算机没有崩溃 . :-)
如果您阅读performance and benchmark stats on this website,您将看到最快的阅读方式(因为阅读,写作和处理都不同),文本文件是以下代码片段:
所有大约9种不同的方法都是基准标记,但是大多数时候这个方法似乎都提前出来,甚至像其他读者所提到的那样执行缓冲读取器 .
对于二进制文件,我发现最快的阅读方式就是这个 .
在我的测试中它快了几百倍 .
使用后台工作程序并只读取有限数量的行 . 仅在用户阅读时阅读更多内容滚动 .
并尝试永远不要使用ReadToEnd() . 它's one of the functions that you think 2602459 ; it'是一个很好的小东西,但正如你所看到的那样,大文件很糟糕......
那些告诉你使用StringBuilder的人需要更频繁地阅读MSDN:
性能注意事项
Concat和AppendFormat方法都将新数据连接到现有的String或StringBuilder对象 . String对象并置操作始终从现有字符串和新数据创建新对象 . StringBuilder对象维护一个缓冲区以容纳新数据的串联 . 如果房间可用,新数据将附加到缓冲区的末尾;否则,分配一个新的较大缓冲区,将原始缓冲区中的数据复制到新缓冲区,然后将新数据附加到新缓冲区 . String或StringBuilder对象的串联操作的性能取决于内存分配发生的频率 .
字符串连接操作始终分配内存,而StringBuilder连接操作仅在StringBuilder对象缓冲区太小而无法容纳新数据时分配内存 . 因此,如果连接固定数量的String对象,则String类更适合并置操作 . 在这种情况下,编译器甚至可以将单个连接操作组合成单个操作 . 如果连接任意数量的字符串,则StringBuilder对象最好用于连接操作;例如,如果循环连接随机数量的用户输入字符串 .
这意味着 huge 内存的分配,交换文件系统的大量使用,模拟你的硬盘驱动器的部分就像RAM内存,但硬盘驱动器是非常慢的 .
StringBuilder选项看起来很适合将系统用作单用户的用户,但是如果有两个或更多用户同时读取大文件,则会出现问题 .
这应该足以让你入门 .
看看下面的代码片段 . 你提到了
Most files will be 30-40 MB
. 这声称在英特尔四核处理器上在1.4秒内读取180 MB:Original Article
你可能最好使用内存映射文件处理here ..内存映射文件支持将在.NET 4中使用(我想...我听说通过其他人谈论它),因此这个使用p的包装器/调用做同样的工作..
Edit: 在MSDN上看到它的工作原理,这里的blog条目表明它在即将发布的.NET 4中是如何完成的 . 我之前给出的链接是pinvoke周围的包装来实现这一点 . 您可以将整个文件映射到内存中,并在滚动文件时将其视为滑动窗口 .
迭代器可能适合这种类型的工作:
您可以使用以下方法调用它:
在加载文件时,迭代器会将进度编号从0返回到100,您可以使用它来更新进度条 . 循环完成后,StringBuilder将包含文本文件的内容 .
此外,因为您需要文本,我们可以使用BinaryReader读取字符,这将确保您的缓冲区在读取任何多字节字符(UTF-8,UTF-16等)时正确排列 .
这些都是在不使用后台任务,线程或复杂的自定义状态机的情况下完成的 .
我的文件超过13 GB:
波纹管链接包含轻松读取文件的代码:
Read a large text file
More information
所有优秀答案!然而,对于寻找答案的人来说,这些似乎有点不完整 .
由于标准字符串只能是大小X,2Gb到4Gb,具体取决于您的配置,这些答案并不能真正满足OP的问题 . 一种方法是使用字符串列表:
有些人可能想要在处理时进行Tokenise和拆分 . 字符串列表现在可以包含非常大量的文本 .