C# - Stream.Read 偏移工作不正确

Posted

技术标签:

【中文标题】C# - Stream.Read 偏移工作不正确【英文标题】:C# - Stream.Read offset is working incorrectly 【发布时间】:2021-09-02 06:26:56 【问题描述】:

我有一个如下所示的测试代码。我正在从流中读取,偏移 2 个位置,然后获取下一个 2 个字节。我希望结果是一个包含 2 个元素的数组。但这不起作用 - 偏移量被完全忽略,并且始终返回完整大小的数组,只有偏移量块具有值。 但这意味着我的结果表仍然很大,它只是有很多不需要的零

如何修改下面的代码,以便当长度 = 2 且偏移量 = 2 时 file.Read() 只返回 2 个字节的数组而不是 10 个?在现实世界的场景中,我正在处理大文件(> 2gigs),因此过滤掉结果数组不是一种选择。

编辑:由于问题尚不清楚 - 下面的代码要求我始终定义与流大小相同的输出数组。相反,我希望有一个大小为length 的输出(在下面的示例中,我希望有var buffer = new byte[2],但这会引发异常,因为 file.Read 忽略偏移量和长度并始终返回 10 个元素(使用仅读取其中 2 个,其余为虚拟零)。

        private byte[] GetFilePart(int length, int offset)
        
            //build some dummy content
            var content = new byte[10];
            for (int i = 0; i<10; i++)
            
                content[i] = 1;
            

            //read the data from content
            var buffer = new byte[10];
            using (Stream file = new MemoryStream(content))
            
                file.Read(buffer, offset, length);
            
            return buffer;
        

【问题讨论】:

我没有听懂你的问题——截图看起来是正确的。它从流中读取 2 个字节,并将它们写入 buffer 中的偏移量 2(因此写入 buffer 中的索引 2 和 3)。你预计会发生什么? 指定你想要的尺寸? new byte[length]; Stream.Read 不会创建新数组,它会填充您传递的数组。因此,如果您希望结果数组的大小为 2,则需要传递一个大小为 2 的数组。另外,check the documentation @canton7 我想定义 var buffer = new byte[2]。但随后代码失败,因为它试图始终填充 10 个元素。 @ojek 我不这么认为——如果length 是 2,它总是会读取 2 个字节。如果您有一个 2 字节的缓冲区,并尝试将 2 个字节读入其中,偏移 2(即写入缓冲区中的索引 2 和 3),那么这当然会失败:您需要一个 4 字节的缓冲区能够写入该缓冲区的索引 2 和 3:这是常识。我认为混淆可能是offset 是您想要开始写入的buffer 的偏移量,而不是您想要开始读取的file 中的偏移量。正如文档所说,“缓冲区中开始存储数据的从零开始的字节偏移量 【参考方案1】:

看起来它对我来说工作正常;如果您使用以下内容初始化内容数组,也许您的困惑会有所消除:

        for (int i = 1; i<=10; i++)
        
            content[i-1] = i;
        

然后每个字节将有不同的数字,图像将如下所示:

offsetbuffer 的位置有关,Stream 将写入字节(它从content 的开头读取)。它与从content 中读取的字节无关。

想象Read 被称为WriteBytesInto(byte[] whatBuffer, int whereToStartWriting, int howManyBytesToWrite) - 你提供它将写入的缓冲区并告诉它从哪里开始以及要执行多少操作

如果您这样做,已将 content 初始化为递增数字:

file.Read(buffer, 2, 3); //read 3 bytes from stream and write to buffer @ index 2
file.Read(buffer, 0, 2); //read 2 bytes from stream and write to buffer @ index 0

你的缓冲区最终看起来像:

4,5,1,2,3,0,0,0,0,0

1,2,3 先写,然后4,5 写下一个


如果您想从流中跳过两个字节(即从流中读取contentSeek() 的第 3 和第 4 字节或设置其Position(或者如 canton7 在评论中建议的那样,如果流是不可搜索,读取并丢弃一些字节)

我如何修改下面的代码,以便当长度 = 2 和偏移量 = 2 时 file.Read() 只返回一个 2 字节而不是 10 字节的数组?

好吧,file.Read 根本不返回数组;它会修改你给它的数组。如果你想要一个 2 字节数组,给它一个 2 字节数组:

byte buf = new byte[2];
file.Read(buf, 0, buf.Length);

如果要打开文件,请跳过前 7 个字节,然后将第 8 和第 9 字节读取到长度为 2 字节的数组中:

byte buf = new byte[2];
file.Position = 7; //absolute skip to 8th byte
file.Read(buf, 0, buf.Length);

有关在流中搜索的更多信息,请参阅Stream.Seek(0, SeekOrigin.Begin) or Position = 0

【讨论】:

(请注意,并非所有流都可以搜索,请检查Stream.CanSeek。如果流不可搜索,您只需读取并丢弃任何要跳过的字节) 是的,虽然可以寻找 MemoryStream :) 可以,但他们也说这是“虚拟内容”,所以我不想对他们的实际用例做任何假设! 此外,OP 混淆看起来与buffer 大小有关。您可以编辑您的答案以解决 Stream.Read 不创建新数组,而是填充您传递给它的数组这一事实。所以,如果 OP 想要一个大小为 2 的缓冲区,他们必须这样做 var buffer = new byte[2]; 谢谢!您的评论“偏移量与 Stream 将字节写入缓冲区的位置有关(它从内容的开头读取)。它与从内容中读取的字节无关”很好地解释了我的困惑。我在想偏移量是针对流源的,你是对的,它是针对输出缓冲区的,现在一切都说得通了,感谢您的澄清!

以上是关于C# - Stream.Read 偏移工作不正确的主要内容,如果未能解决你的问题,请参考以下文章

C# 从 UTF-8 流中读取字符串的正确方法

C#文件流写入方法

如果没有发送任何内容,Stream.Read不会返回任何内容

jQuery 偏移顶部无法正常工作

C# 通过tcp协议向硬件发送命令,networkstream read 读取返回信息时卡死。

当 Stream.Read() 存在时,StreamReader 的目的是啥?