File.ReadLines(文件).Skip(numLines)如何工作?

时间:2015-01-26 10:10:48

标签: c#

我有一个相当大的文件,我希望从特定的行读取。我找到了

File.ReadLines(file).Skip(numLines);

效果很好。但是,我不明白这是如何在表面下工作的。我写了几个基本的基准测试,看看是否有一些同事建议的性能差异。我测试的方法是:

  1. StreamReader用于读取到此为止的所有行:

    public string streamToLine(int lineNumber)
    {
        StreamReader reader = new StreamReader(fileName);
    
        for (int i = 0; i < lineNumber - 1; i++)
        {
            reader.ReadLine();
        }
    
        string line = reader.ReadLine();
        reader.Close();
    
        return line;
    }
    
  2. File.ReadLines(file)并使用枚举器迭代到该行:

    public string readToLine(int lineNumber)
    {
        IEnumerator<string> lines = File.ReadLines(fileName).GetEnumerator();
        for (int i = 0; i < lineNumber; i++)
        {
            lines.MoveNext();
        }
        return lines.ToString();
    }
    
  3. 使用跳过功能:

    public string skipToLine(int lineNumber)
    {
        IEnumerator<string> lines = File.ReadLines(fileName).Skip(lineNumber-1).GetEnumerator();
    
        return lines.ToString();
    }
    
  4. 我在一个包含1000万行的文件上运行了10次测试,试图读取第9百万行并平均显示这需要多长时间(以毫秒为单位):

    Stream To Line:2442.1

    读取行:2534.9

    跳至行:0

    看起来Skip甚至不考虑lineNumber之前的其他行,并且确切知道第9百万行的确切位置。它是否以某种方式从文件中推断出来?其他2个方法处理行的方式是否有一些开销,因为它们返回读取的内容?怎么会有这么大的差异?

2 个答案:

答案 0 :(得分:8)

基本上,问题是你的测试。你还没有在调查器上调用MoveNext(),所以它还没有做任何事情。迭代器通常是延迟和流式传输,尤其是在LINQ的情况下。

顺便说一句,您需要使用GetEnumerator() 非常罕见;访问此类数据的惯用方法是foreach

如果你想看到这个:

    static void Main()
    {
        using(var iter = GetData().GetEnumerator())
        {
            System.Console.WriteLine("Have iterator");
            while(iter.MoveNext())
            {
                System.Console.WriteLine(iter.Current);
            }
            System.Console.WriteLine("Done");
        }
    }
    static IEnumerable<int> GetData()
    {
        System.Console.WriteLine("Before doing anything");
        yield return 1;
        yield return 2;
        yield return 3;
        System.Console.WriteLine("Ater doing everything ");
    }

你应该注意到"Have iterator"是在 "Before doing anything"之前编写的,它告诉我们一个人可以拥有一个尚未完成任何事情的迭代器。它是第一个使它打印的MoveNext()

答案 1 :(得分:0)

它是因为另一个实际读取了这一行,但是最后一行没有读取它自己的行,它只返回一个可枚举的,并且可枚举的函数从第一行到第十行运行代码然后停下来,他们工作直到达到收益,然后他们将控制权交还给用户,然后让用户决定他是否想要阅读...如果你通过,它就停止,如果你再次打电话,它会继续最后一个地方,... ...

在你写完这个答案之后,你有些得到了我,因为我仍然是我自己的担心&#34;如何采取零?这个,寻找一些方法,绕过&#34;,直到我刷新并注意到第一个答案,告诉你没有调用MoveNext,我没有注意到,......当你将umeume兑换成ienumerator,...枚举是foreach使用的东西,任何集合实现它,但是枚举器就像sql数据读取器一样,它们逐个移动到下一个项目,你需要每次检查,如果有更多的项目,所以你没有得到列表,你得到类枚举器的对象,它提供了读取方法,但它自己不做任何事情