C# 从C调用ReadLine()后,FileStream位置已关闭#
我试图一次读取一个(小的ish)文件,一次读取几行,我需要返回到特定块的开头 问题是,在第一次打电话给C# 从C调用ReadLine()后,FileStream位置已关闭#,c#,filestream,readline,C#,Filestream,Readline,我试图一次读取一个(小的ish)文件,一次读取几行,我需要返回到特定块的开头 问题是,在第一次打电话给 streamReader.ReadLine(); streamReader.BaseStream.Position属性设置为文件的结尾!现在我假设一些缓存是在后台完成的,但是我希望这个属性反映从该文件使用的I字节数。是的,该文件有多行:-) 例如,再次调用ReadLine() 我怎样才能找到第1行结束的实际位置,以便稍后返回 我只能考虑通过添加ReadLine()返回的字符串的长度来手动记账
streamReader.ReadLine();
streamReader.BaseStream.Position
属性设置为文件的结尾!现在我假设一些缓存是在后台完成的,但是我希望这个属性反映从该文件使用的I字节数。是的,该文件有多行:-)
例如,再次调用ReadLine()
我怎样才能找到第1行结束的实际位置,以便稍后返回
我只能考虑通过添加ReadLine()返回的字符串的长度来手动记账,但即使在这里,也有几个注意事项:
- ReadLine()带出可能具有可变长度的新行字符(是“\n”?是“\r\n”?等)
- 我不确定这是否适用于可变长度字符
…所以现在看来我唯一的选择就是重新思考如何解析文件,这样我就不必倒带了
如果有帮助,我会按如下方式打开文件:
using (var reader = new StreamReader(
new FileStream(
m_path,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite)))
{...}
有什么建议吗?StreamReader
不是为这种用途设计的,所以如果这是您需要的,我想您必须为文件流编写自己的包装器,如果您需要读取行,并且需要返回到以前的块,为什么不将您读取的行存储在列表中?这应该很容易
您不应该依赖于根据字符串的长度计算字节长度——因为您提到的原因:多字节字符、换行符等。我做过类似的实现,需要快速访问超大文本文件中的第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();
}
另请注意,已经存在缓冲机制FileStream
公认答案的一个问题是,如果ReadLine()遇到异常,例如由于日志框架在您ReadLine()时临时锁定了文件,那么您将不会将该行“保存”到列表中,因为它从未返回一行。如果捕获到此异常,则无法再次重试ReadLine(),因为StreamReaders的内部状态和缓冲区与上一个ReadLine()相比发生了错误,您将只返回部分行,并且您不能忽略该断行,并在OP发现时返回到其开头
如果您想找到真正的可查找位置,那么您需要使用反射来找到StreamReaders私有变量,这些变量允许您计算它在自己的缓冲区中的位置。这里看到的格兰杰解:,应该是有效的。或者做其他相关问题中的其他答案所做的事情:创建自己的StreamReader,公开真正可查找的位置(此链接中的答案:)。这是我在处理StreamReader和seeking时遇到的唯一两个选项,出于某种原因,这两个选项决定在几乎所有情况下都完全消除查找的可能性
编辑:我使用了格兰杰的解决方案,它是有效的。只需确保按照以下顺序执行:GetActualPosition(),然后将BaseStream.Position设置为该位置,然后确保调用DiscardBufferedData(),最后可以调用ReadLine(),这样就可以从方法中给定的位置开始获取整行。存在问题。处理BOM是一件大事。