【问题标题】:StreamReader.BaseStream issue after using EndOfStream property使用 EndOfStream 属性后的 StreamReader.BaseStream 问题
【发布时间】:2016-02-27 10:01:20
【问题描述】:

首先我明白我可以使用不同的方式解决这个问题。我猜这个问题的存在只是因为以不正确的方式使用了不同的方法。但我想知道我的例子中到底发生了什么。

我使用 StreamReader 来读取文件。为了从中获取字节,我决定使用 BaseStream.Read:

        int length = (int)reader.BaseStream.Length;
        byte[] file = new byte[length];
        while(!reader.EndOfStream)
        {
            int readBytes = reader.BaseStream.Read(file, 0, 
                (length-offset)>bufferSize?bufferSize:(length - offset));
            for (int i = 0; i<readBytes; i++)
            {
                ...
            }
            offset += readBytes;
        }

在读取之前使用属性 StreamReader.EndOfStream 时,BaseStream.Read 拒绝获取最后 1024 个字节。后来我发现信息,EndOfStream 试图读取 1 个字节,但实际上他读取了 1024 个字节,因为性能。显然这 1kb 是不可能达到的。

编辑:如果我删除代码中的 reader.EndOfStream 属性,reader.BaseStream.Read 将正常工作。这是主要问题。

我再次理解,这个代码示例绝对是低效的。我只是想了解流在该示例中是如何工作的,并且是否仅因为错误代码而存在此问题(或 StreamReader.BaseStream 有一些问题)?提前致谢。

【问题讨论】:

  • 你为什么要使用 StreamReader?
  • @usr 就像我说的,完全可以毫无困难地避免这个问题。我只是好奇使用属性的事实如何以这种奇怪的方式影响内部流。

标签: c# stream filestream streamreader


【解决方案1】:

不是StreamReader.BaseStream 有一些问题,而是您的代码有问题。当您直接使用包裹在StreamReader 中的Stream 时。

来自 MSDN 关于StreamReader.DiscardBufferedData

只有当内部缓冲区的位置和 BaseStream 的位置不匹配时才需要调用该方法。当您将数据读入缓冲区然后在底层流中寻找新位置时,这些位置可能会变得不匹配。

这意味着,在您的情况下,当 Stream 已经到达结束位置时,StreamReader internal buffer 的位置在您直接读取基础流之前仍然保持不变,因此 @987654327 @ 仍然 = false。这就是你无法完成循环的原因。

编辑:

我觉得你遗漏了什么,我给你这段代码来证明文件成功到达最后。运行它,你会看到你的应用重复说:I'm at the end of the file!

static void Main()
{
    using (StreamReader reader = new StreamReader(@"yourFile"))
    {
        int offset = 0;
        int bufferSize = 102400;
        int length = (int)reader.BaseStream.Length;
        byte[] file = new byte[length];
        while (!reader.EndOfStream)
        {


            // Add this line:
            Console.WriteLine(reader.BaseStream.Position);
            Console.ReadLine();



            int readBytes = reader.BaseStream.Read(file, 0,
                (length - offset) > bufferSize ? bufferSize : (length - offset));
            string str = Encoding.UTF8.GetString(file, 0, readBytes);
            offset += readBytes;
            if (reader.BaseStream.Position == length)
            {
                Console.WriteLine("I'm at the end of the file!  Current Tickcount: " + Environment.TickCount);
                Thread.Sleep(100);
            }
        }
    }
}

编辑 2

但是,偏移量和长度应该相等,我的情况是长度 - 偏移量 = 1024(如果文件大于 1kb)。也许我做错了什么,但如果我使用小于 1kb 的文件,readBytes 总是等于 0。

因为你第一次调用while (!reader.EndOfStream),所以读者必须读取文件(这种情况是 1024 字节 - 读取字节到内部缓冲区)来确定文件是否结束 (参见两行代码我在上面添加),在它读取文件后寻找 1024 字节,这就是为什么 length - offset = 1024,如果你的文件小于 1kb,那么第一次调用时,它已经在寻找文件结尾。这是您丢失数据的地方。

第二次调用它,它不寻找,因为你没有向阅读器发送任何读取请求,所以它认为没有改变,那么它不需要再次读取文件来检查文件是否在末尾,这就是为什么第二次通话不会丢失数据的原因。

【讨论】:

  • 感谢您的回答。我知道 StreamReader 有内部缓冲区和他自己的位置。但是我完全感到不安的是,最后 1024 个字节的可用性取决于使用 EndOfStream property (而不是方法,其目的是改变对象的状态)。问题是,如果我在代码中删除 reader.EndOfStream,所有数据都将可用。使用 StreamReader 之类的包装器会影响已包装的 Stream 中数据的可访问性。所以我仍然不明白包装器究竟是如何影响内部流的,以及为什么它使最后 1024 个字节无法访问。
  • 感谢编辑。你说得对,我的应用程序说 我在文件末尾!。但是,offsetlength 应该相等,我的情况是 length - offset = 1024(如果文件大于 1kb)。也许我做错了什么,但如果我使用小于 1kb 的文件,readBytes 总是等于 0。
  • 感谢您的解释。所以,一般来说,当EndOfStream 被调用时,Stream.Positon 会增加 1024,如果可能的话(如果有必要的话)。而且由于BaseStream使用不当,会导致数据泄露。我做对了吗?
猜你喜欢
  • 1970-01-01
  • 2023-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-08
  • 1970-01-01
  • 2017-04-15
  • 1970-01-01
相关资源
最近更新 更多