c# FileStream Read 与 StreamReader EndOfStream 有问题

c# FileStream Read having problems with StreamReader EndOfStream

正如标题所说,我发现了一个问题。先说小故事: 我们有 file.txt 看起来像这样:

aaaabb
ccccddd
eeeefffffff

阅读这篇文章有很多方法line-by-line,其中一种是:

StreamReader sr = new StreamReader("file.txt");
while(!sr.EndOfStream)
{
    string s = sr.ReadLine();
}
sr.Close();

有效。 s 获取每一行。 现在我需要前 4 个字母作为字节,其余的作为字符串。在查阅资料并进行一些试验后,我发现最简单的方法是这样的:

FileStream fs = new FileStream("file.txt", FileMode.Open);
StreamReader sr = new StreamReader(fs);
byte[] arr = new byte[4];
fs.Read(arr, 0, 4);
string s = sr.ReadLine();
sr.Close();
fs.Close();

有效。 arr 包含前 4 个字母作为字节,该行的其余部分保存在 s 中。这只是一行。如果我们添加 while:

FileStream fs = new FileStream("file.txt", FileMode.Open);
StreamReader sr = new StreamReader(fs);
while(!sr.EndOfStream)
{
    byte[] arr = new byte[4];
    fs.Read(arr, 0, 4);
    string s = sr.ReadLine();
} 
sr.Close();
fs.Close();

现在有问题了。现在 arr 没有得到任何东西, s 读取整行,包括前 4 个字母。更奇怪的是,如果我使用 while(true) (并且我假设任何其他不是示例的东西)而不是按预期工作,4 个字符作为字节,其余的是字符串,并且每一行都是相同的。

问题是我错过了什么?为什么会这样?我该如何解决这个问题?或者这可能是一个错误?

这里的问题是简单的缓冲。当您将 StreamReader 附加到 FileStream 时,它最终会消耗文件中的一个块,从而推进 FileStream 的当前 Position。使用您的示例文件和默认缓冲区大小,一旦 StreamReader 附加自身,它基本上将整个文件消耗到缓冲区中,将 FileStream 留在其 EOF 处。当您然后尝试通过 fs 引用直接从 FileStream 读取 4 个字节时,没有任何东西可以消耗。以下 ReadLine 适用于您的 sr 参考,因为它是从缓冲文件内容中读取的。

下面是对正在发生的事情的逐步分解:

  1. fs 打开文件并位于 Position 0.
  2. sr 结束 fs 并且对 EndOfStream 的调用最终消耗(在本例中)27 个字节到其内部缓冲区中。此时,fs Position 现在位于 EOF。
  3. 您尝试直接从 fs 读取,但它在 EOF 处没有更多字节。
  4. sr.ReadLine 从它在步骤 #2 中建立的缓冲区中读取并且一切正常。

要修复特定的错误情况,您可以将字节数组更改为字符数组并改用 sr.Read。即

char[] arr = new char[4];
sr.Read(arr, 0, 4);

Now there's a problem. Now arr doesn't get anything and s reads the whole line including the first 4 letters.

是的,这似乎很有道理。 StreamReader 维护一个缓冲区 - 当您要求它读取一行文本时,它很可能从流中读取 更多 比那一行,在下一行时使用该缓冲数据询问信息。

从根本上说,我强烈建议直接从 StreamReader 正在读取的流中读取。即使在可能的情况下,也很难做到正确,在某些情况下,API 可能不会让你做你想做的事。

如果要删除每行的前四个字符,阅读整行会更简单,然后使用Substring