3

根据微软对ReadLinesandReadAllLines方法的解释,当你使用 时ReadLines,你可以在返回整个集合之前开始枚举字符串集合。使用 时ReadAllLines,必须等待返回整个字符串数组才能访问该数组。因此,当您处理非常大的文件时,ReadLines效率会更高。

当他们说:

1 - “当你使用ReadLines时,你可以在返回整个集合之前开始枚举字符串的集合。 ” 如果写了下面这行代码,那么是不是意味着ReadLines方法执行结束了,整个集合是返回并存储在变量文件数据中?

IEnumerable<String> filedata = File.ReadLines(fileWithPath)

2-“使用时ReadAllLines必须等待返回整个字符串数组才能访问数组”。这是否意味着,在下面的代码片段中,如果读取了一个大文件,那么如果在读取文件后立即使用数组变量 hugeFileData 将不会包含所有数据?

string[] hugeFileData = File.ReadAllLines(path)
string i = hugeFileData[hugeFileData.length-1];

3 - “当您处理非常大的文件时,ReadLines 可以更高效”。如果是这样,下面的代码在读取大文件时是否有效?我相信下面的代码的第 2 行和第 3 行会读取文件两次,如果我错了,请纠正我。

string fileWithPath = "some large sized file path";
string lastLine = File.ReadLines(fileWithPath).Last();
int totalLines = File.ReadLines(fileWithPath).Count();

在上面的代码片段中两次在同一个文件上调用 ReadLines 的原因是,当我尝试下面的代码时,TextReader在下面的代码片段的第 3 行出现异常“无法从已关闭的文件中读取”。

IEnumerable<String> filedata = File.ReadLines(fileWithPath);
string lastLine = filedata.Last();
int totalLines = filedata.Count();
4

5 回答 5

7

ReadLines和之间的区别ReadAllLines很容易用代码说明。

如果你这样写:

foreach (var line in File.ReadLines(filename))
{
    Console.WriteLine(line);
}

发生的情况与此类似:

using (var reader = new StreamReader(filename))
{
    while (!reader.EndOfStream)
    {
        var line = reader.ReadLine();
        Console.WriteLine(line);
    }
}

生成的实际代码稍微复杂一些(ReadLines返回一个枚举器,其MoveNext方法读取并返回每一行),但从外部看,行为是相似的。

这种行为的关键是延迟执行,为了充分利用 LINQ,您应该很好地理解这一点。所以你的第一个问题的答案是“不”。所有的调用ReadLines都是打开文件并返回一个枚举器。在您要求之前,它不会读取第一行。

请注意,代码可以在读取第二行之前输出第一行。此外,您一次只使用一行内存。

ReadAllLines有很多不同的行为。当你写:

foreach (var line in File.ReadAllLines(filename))
{
    Console.WriteLine(line);
}

实际发生的更像是这样的:

List<string> lines = new List<string>();
using (var reader = new StreamReader(filename))
{
    while (!reader.EndOfStream)
    {
        var line = reader.ReadLine();
        lines.Add(line);
    }
}
foreach (var line in lines)
{
    Console.WriteLine(line);
}

在这里,程序必须先将整个文件加载到内存中,然后才能输出第一行。

你使用哪一个取决于你想做什么。如果您只需要逐行访问文件,那么ReadLines通常是更好的选择——尤其是对于大文件。但是,如果您想随机访问行,或者您将多次读取文件,那么ReadAllLines可能会更好。但是,请记住,这ReadAllLines要求您有足够的内存来保存整个文件。

在你的第三个问题中,你展示了这段代码,它在最后一行产生了一个异常:

IEnumerable<String> filedata = File.ReadLines(fileWithPath);
string lastLine = filedata.Last();
int totalLines = filedata.Count();

这里发生的是第一行返回了一个枚举数。第二行代码枚举了整个序列(即读取到文件末尾),以便找到最后一行。枚举器看到它在文件末尾并关闭了相关的阅读器。最后一行代码再次尝试枚举文件,但文件已经关闭。返回的枚举器中没有“重置到文件开头”功能ReadLines

于 2014-07-23T17:13:53.633 回答
3
  1. 不,在程序中,需要从磁盘读取文件的零行并将其存储在内存中。直到您要求第一行(您还没有要求该片段中的一行),它才需要获取第一行。直到您要求该行之后,它才需要获取第二行,依此类推。

  2. 该程序将要求将整个文件一次全部读入内存,以便获取最后一行。如果您有 3 GB 的文件,则需要 3 GB 的内存。

  3. 是的,第一个片段将读取整个文件两次,而无需在任何时间点在内存中存储超过一行。该程序的内存占用将是 O(1),而不是取决于程序的大小。它确实需要通读整个程序从开始到结束两次,因此执行可能需要更长的时间,但与之前显示的代码段相比,它消耗的内存要少得多当然,有一些方法ReadLines可以同时计算行数并获取最后一行,而无需两次遍历序列,这是您真正应该做的,这样您就可以两全其美。

于 2014-07-23T17:14:41.937 回答
1

ReadLines()方法使用枚举器仅在需要时读取每一行,因此这样的代码可以工作,因为该方法根据需要获取每一行:

foreach (string line in File.ReadLines("c:\\file.txt"))
{
    Console.WriteLine("-- {0}", line);
}

如果文件很大,该ReadLines()方法很有用,因为它不需要一次将所有数据保存在内存中。此外,如果您的程序提前退出循环,ReadLines()则更好,因为不需要进一步的 I/O。

ReadAllLines()方法将整个文件读入内存,然后返回这些行的数组。

于 2014-07-23T17:14:49.167 回答
0
  1. 是的,该方法已完成执行。不,执行还没有结束。返回的可枚举具有从文件中读取并为您提供行的所有必要数据和行为。
  2. 完成File.ReadAllLines后,整个文件已被读取。string[] 不能延迟返回结果。因此,仅从您看到的那种File.ReadAllLines急切地执行所有工作的类型。
  3. 是的,您正在阅读该文件两次。不必如此。遍历循环返回的行,维护一个计数器和看到的最后一行。这使您可以一次通过文件计算这两个值。
于 2014-07-23T17:14:58.337 回答
0

您可以像这样使用 ReadLines:

foreach (string line in File.ReadLines(fileWithPath))
{
    if (line.Contains("bla bla") & line.Contains("do do"))
    {

    }
    totalLines += 1;
}

在访问数组之前,您无需等待返回整个字符串数组。与您在继续之前加载整个数组的情况不同:

string[] readText = File.ReadAllLines(path);
foreach (string s in readText)
{
    Console.WriteLine(s);
}
于 2014-07-23T17:17:59.863 回答