What's the fastest way to read a text file line-by-line?
我想一行一行地阅读文本文件。我想知道我是否在.NET C工作范围内尽可能高效地完成这项工作。
这就是我目前正在尝试的:
1 2 3 4 5 6 7 8 9 10 | 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 } |
要找到逐行读取文件的最快方法,您必须进行一些基准测试。我在我的计算机上做了一些小测试,但是你不能期望我的结果适用于你的环境。
使用streamreader.readline
这基本上是你的方法。出于某种原因,您将缓冲区大小设置为可能的最小值(128)。增加这一点通常会提高性能。默认大小为1024,其他好的选择是512(Windows中的扇区大小)或4096(NTFS中的群集大小)。您必须运行一个基准来确定最佳缓冲区大小。一个更大的缓冲区——如果不是更快的话——至少不会比一个更小的缓冲区慢。
1 2 3 4 5 6 7 | 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 } |
使用file.readlines
这与您自己的解决方案非常相似,只是它是使用固定缓冲区大小为1024的
1 2 3 | var lines = File.ReadLines(fileName); foreach (var line in lines) // Process line |
使用file.readalllines
这与前面的方法非常相似,只是这个方法增加了用于创建返回的行数组的字符串列表,因此内存需求更高。但是,它返回
1 2 3 4 5 | var lines = File.ReadAllLines(fileName); for (var i = 0; i < lines.Length; i += 1) { var line = lines[i]; // Process line } |
使用string.split
这种方法速度要慢得多,至少在大文件上(在511kb文件上测试),这可能是由于
1 2 3 4 5 6 7 | using (var streamReader = File.OpenText(fileName)) { var lines = streamReader.ReadToEnd().Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) // Process line } |
我的建议是使用
如果您使用的是.NET 4,只需使用
虽然
如果你只是想在不做太多事情的情况下读取一个文件中的行,根据这些基准,读取文件的最快方法是古老的方法:
1 2 3 4 5 6 7 8 | using (StreamReader sr = File.OpenText(fileName)) { string s = String.Empty; while ((s = sr.ReadLine()) != null) { //do minimal amount of work here } } |
但是,如果您需要对每行做很多工作,那么本文总结出最好的方法如下(如果您知道要读多少行,那么预先分配一个字符串[]会更快):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 }); |
使用以下代码:
1 | foreach (string line in File.ReadAllLines(fileName)) |
这是一个巨大的差异在阅读性能。
它以消耗内存为代价,但完全值得!
关于这个栈内溢出问题有一个很好的话题,"收益率回报"比"老派"回报慢吗?.
它说:
ReadAllLines loads all of the lines into memory and returns a
string[]. All well and good if the file is small. If the file is
larger than will fit in memory, you'll run out of memory.ReadLines, on the other hand, uses yield return to return one line at
a time. With it, you can read any size file. It doesn't load the whole
file into memory.Say you wanted to find the first line that contains the word"foo",
and then exit. Using ReadAllLines, you'd have to read the entire file
into memory, even if"foo" occurs on the first line. With ReadLines,
you only read one line. Which one would be faster?
如果文件大小不大,则读取所有文件的速度更快,然后拆分字符串:
1 2 | var filestreams = sr.ReadToEnd().Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); |
如果您想使用现有的API来读取这些行,就不能再快了。但是读取更大的块并在读取缓冲区中手动查找每一行可能会更快。
如果您有足够的内存,我发现通过将整个文件读取到内存流中,然后打开一个流阅读器来读取行,可以获得一些性能提升。只要你真的打算读整个文件,这会带来一些改进。