需要帮助理解Microsoft对File.ReadLines和File.ReadAllLines的解释

时间:2014-07-23 17:08:09

标签: c# readlines file.readalllines

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

当他们说:

时,它实际上是什么意思

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

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

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

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

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

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

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

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

5 个答案:

答案 0 :(得分:7)

代码可以很容易地说明ReadLinesReadAllLines之间的差异。

如果你这样写:

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方法读取并返回每一行),但从外部来看,行为类似。

该行为的关键是deferred execution,为了充分利用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返回的枚举器中没有“重置到文件的开头”功能。

答案 1 :(得分:3)

  1. 没有。在程序中的那一点,文件的零行需要从磁盘读取并存储在内存中。直到你要求第一行(你还没有在该片段中要求一行)它才需要获取第一行。直到你之后要求获取第二行的行,等等。

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

  3. 是的,第一个代码段将读取整个文件两次,无需在任何时间点在内存中存储多行。该程序的内存占用量为O(1),而不是取决于程序的大小。它确实需要读取整个程序开始两次完成,因此执行可能需要更长时间,但它会比你之前显示的代码消耗大大更少的内存。当然,有一些方法可以使用ReadLines来计算行数并获取最后一行而不需要遍历序列两次,这是你应该真正做到的,这样你就可以充分利用这两个世界。

答案 2 :(得分:1)

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

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

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

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

答案 3 :(得分:0)

  1. 是的,该方法已完成执行。不,执行还没有结束。返回的可枚举具有从文件中读取并传递给您的所有必要数据和行为。
  2. 完成File.ReadAllLines后,已读取整个文件。字符串[]不能懒惰地返回结果。所以,从您看到File.ReadAllLines急切执行所有工作的类型开始。
  3. 是的,你正在读两次文件。不一定如此。在循环返回的行上运行,维护计数器并看到最后一行。这允许您通过文件一次计算两个值。

答案 4 :(得分: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);
}