从C#调用ReadLine()后FileStream位置关闭

时间:2010-05-28 16:40:47

标签: c# filestream readline

我正在尝试一次读取几行中的(小型)文件,我需要返回到特定块的开头。

问题是,在第一次调用

之后
streamReader.ReadLine();

streamReader.BaseStream.Position属性设置为文件末尾!现在我假设在后台完成了一些缓存,但我希望这个属性能够反映 I 从该文件中使用的字节数。是的,该文件有多行:-)

例如,再次调用ReadLine()将(自然地)返回文件中的下一行,该行不会从streamReader.BaseStream.Position之前报告的位置开始。

如何找到第一行结束的实际位置,以便稍后返回?

我只能通过添加ReadLine()返回的字符串长度来考虑手动执行簿记,但即使在这里也有一些注意事项:

  • ReadLine()删除可能具有可变长度的新行字符(是'\ n'?是“\ r \ n”?等等)
  • 我不确定这是否适用于可变长度字符

...所以现在似乎我唯一的选择是重新思考我如何解析文件,所以我不必倒带。

如果有帮助,我打开我的文件:

using (var reader = new StreamReader(
        new FileStream(
                       m_path, 
                       FileMode.Open, 
                       FileAccess.Read, 
                       FileShare.ReadWrite)))
{...}

有什么建议吗?

4 个答案:

答案 0 :(得分:4)

如果您需要读取行,并且需要返回上一个块,为什么不将您在行列中读取的行存储?这应该很容易。

你不应该依赖于根据字符串的长度计算长度(以字节为单位) - 由于你自己提到的原因:多字节字符,换行字符等。

答案 1 :(得分:4)

我做了类似的实现,我需要快速访问极大文本文件中的第n行。

streamReader.BaseStream.Position指向文件末尾的原因是它有一个内置缓冲区,正如您所期望的那样。

通过计算从每个ReadLine()调用中读取的字节数进行的簿记将适用于大多数纯文本文件。但是,我遇到的情况是文本文件中混合了控制字符,不可打印的字符。计算出的字节数是错误的,导致我的程序无法在此后寻找正确的位置。

我的最终解决方案是自己实施线路阅读器。到目前为止它运作良好。这应该给出一些想法:

using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
    int ch;
    int currentLine = 1, offset = 0;

    while ((ch = fs.ReadByte()) >= 0)
    {
        offset++;

        // This covers all cases: \r\n and only \n (for UNIX files)
        if (ch == 10)
        {
            currentLine++;

            // ... do sth such as log current offset with line number
        }
    }
}

然后回到记录的偏移量:

using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
    fs.Seek(yourOffset, SeekOrigin.Begin);
    TextReader tr = new StreamReader(fs);

    string line = tr.ReadLine();
}

另请注意,已存在缓冲机制built into FileStream

答案 2 :(得分:2)

StreamReader不适用于此类用法,因此,如果您需要这些,我怀疑您必须为FileStream编写自己的包装。

答案 3 :(得分:1)

接受答案的一个问题是,如果ReadLine()遇到异常,比如由于日志记录框架在ReadLine()时暂时锁定文件,那么你将没有那条线"已保存&#34 ;进入列表,因为它从未返回一行。如果您捕获此异常,则无法再次重试ReadLine(),因为StreamReaders内部状态和缓冲区从最后一个ReadLine()中被搞砸,并且您将只返回返回的一部分行,并且您不能忽略该断行并寻求当OP发现时,回到它的开头。

如果你想到达真正的可搜索位置,那么你需要使用反射来获取StreamReaders私有变量,这些变量允许你计算它在自己的缓冲区中的位置。格兰杰在这里看到的解决方案:StreamReader and seeking,应该可行。或者做其他相关问题中的其他答案:创建自己的StreamReader,公开真正的可搜索位置(此链接中的答案:Tracking the position of the line of a streamreader)。这是我在处理StreamReader和寻求时遇到的唯一两个选项,由于某种原因决定完全消除在几乎所有情况下寻求的可能性。

编辑:我使用了格兰杰的解决方案并且有效。请确保按顺序执行:GetActualPosition(),然后将BaseStream.Position设置为该位置,然后确保调用DiscardBufferedData(),最后可以调用ReadLine(),然后从位置开始获取完整的行在方法中给出。