什么是逐行读取文本文件的最快方法?

我想逐行阅读一个文本文件。 我想知道如果我在.NET C#范围内尽可能有效地做到这一点。

这是我到目前为止的尝试:

var filestream = new System.IO.FileStream(textFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite); var file = new System.IO.StreamReader(filestream, System.Text.Encoding.UTF8, true, 128); while ((lineOfText = file.ReadLine()) != null) { //Do something with the lineOfText } 

如果您使用的是.NET 4,只需使用File.ReadLines即可。 我怀疑它和你的一样,除了它也可能使用FileOptions.SequentialScan和一个更大的缓冲区(128似乎很小)。

要找到逐行读取文件的最快方法,您将不得不做一些基准测试。 我在计算机上做了一些小测试,但是你不能期望我的结果适用于你的环境。

使用StreamReader.ReadLine

这基本上是你的方法。 出于某种原因,您将缓冲区大小设置为可能的最小值(128)。 增加这一般将提高性能。 默认的大小是1,024,其他好的选择是512(Windows中的扇区大小)或4,096(NTFS中的簇大小)。 您将不得不运行基准来确定最佳缓冲区大小。 更大的缓冲区 – 如果不是更快 – 至少不会比更小的缓冲区慢。

 const Int32 BufferSize = 128; using (var fileStream = File.OpenRead(fileName)) using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize)) { String line; while ((line = streamReader.ReadLine()) != null) // Process line } 

FileStream构造函数允许你指定FileOptions 。 例如,如果从头至尾依次读取大文件,则可能会从FileOptions.SequentialScan受益。 再次,基准是你能做的最好的事情。

使用File.ReadLines

这非常类似于您自己的解决方案,只不过它是使用固定缓冲区大小为1,024的StreamReader实现的。 在我的计算机上,与缓冲区大小为128的代码相比,性能稍好。但是,通过使用更大的缓冲区大小,您可以获得相同的性能提升。 这个方法是使用一个迭代器块实现的,并且不消耗所有行的内存。

 var lines = File.ReadLines(fileName); foreach (var line in lines) // Process line 

使用File.ReadAllLines

这和前面的方法非常相似,只不过这个方法增加了一个字符串列表,用于创建返回的行数组,所以内存需求更高。 但是,它返回String[]而不是IEnumerable<String>允许您随机访问这些行。

 var lines = File.ReadAllLines(fileName); foreach (var line in lines) // Process line 

使用String.Split

这种方法相当慢,至少在大文件(在511 KB文件上测试),可能是由于如何实现String.Split 。 它还为所有行分配了一个数组,增加了与解决方案相比所需的内存。

 using (var streamReader = File.OpenText(fileName)) { var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) // Process line } 

我的建议是使用File.ReadLines因为它干净而高效。 如果您需要特殊的共享选项(例如使用FileShare.ReadWrite ),则可以使用自己的代码,但应该增加缓冲区大小。

虽然File.ReadAllLines()是读取文件最简单的方法之一,但它也是最慢的一种。

如果你只是想读一个文件中的行而不做太多, 根据这些基准 ,读取文件的最快方法是古老的方法:

 using (StreamReader sr = File.OpenText(fileName)) { string s = String.Empty; while ((s = sr.ReadLine()) != null) { //do minimal amount of work here } } 

但是,如果你必须对每一行做很多事情,那么本文的结论是,最好的办法是如下(如果你知道要读取多少行,预先分配一个字符串[]会更快):

 AllLines = new string[MAX]; //only allocate memory here using (StreamReader sr = File.OpenText(fileName)) { int x = 0; while (!sr.EndOfStream) { AllLines[x] = sr.ReadLine(); x += 1; } } //Finished. Close the file //Now parallel process each line in the file Parallel.For(0, AllLines.Length, x => { DoYourStuff(AllLines[x]); //do your work here }); 

使用下面的代码:

 var lines = File.ReadAllLines(fileName); foreach (var line in lines) 

阅读表现差异很大。

这是以内存消耗为代价的,但完全值得!

如果文件大小不大,读取所有文件的速度会更快,然后拆分字符串:

 var filestreams = sr.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); 

在堆栈溢出问题中有一个好主题是“收益回报”比“旧学校”回报慢吗?

它说:

ReadAllLines将所有行加载到内存中并返回一个字符串[]。 一切顺利,如果文件很小。 如果文件大于内存容量,则会导致内存不足。

另一方面,ReadLines使用yield return来一次返回一行。 有了它,你可以阅读任何大小的文件。 它不会将整个文件加载到内存中。

假设你想找到包含单词“foo”的第一行,然后退出。 使用ReadAllLines,即使在第一行出现“foo”,也必须将整个文件读入内存。 使用ReadLines,您只能阅读一行。 哪一个会更快?

如果你的目标是执行速度,是的。 使用StreamReader构造函数可以缩短代码。

如果你有足够的内存,通过将整个文件读入内存流 ,然后打开一个流读取器来读取这些行,我发现了一些性能提升。 只要你实际上计划阅读整个文件,这可以产生一些改进。

如果您想使用现有的API来读取这些行,则无法获得更快的速度。 但是,读取较大的块并手动查找读取缓冲区中的每个新行可能会更快。