一种消耗(所有字节)BinaryReader 的优雅方式?

Posted

技术标签:

【中文标题】一种消耗(所有字节)BinaryReader 的优雅方式?【英文标题】:An elegant way to consume (all bytes of a) BinaryReader? 【发布时间】:2012-01-26 15:27:42 【问题描述】:

BinaryReader 模拟StreamReader.ReadToEnd 方法有什么优雅的吗?也许将所有字节放入一个字节数组中?

我这样做:

read1.ReadBytes((int)read1.BaseStream.Length);

...但必须有更好的方法。

【问题讨论】:

我认为毕竟这是一种优雅的赌注方式 【参考方案1】:

遇到同样的问题。 首先,使用 FileInfo.Length 获取文件的大小。 接下来,创建一个字节数组并将其值设置为 BinaryReader.ReadBytes(FileInfo.Length)。 例如

var size = new FileInfo(yourImagePath).Length;
byte[] allBytes = yourReader.ReadBytes(System.Convert.ToInt32(size));

【讨论】:

【参考方案2】:

我使用它,它利用底层的BaseStream 属性为您提供所需的长度信息。它使事情变得简单而美好。

下面是BinaryReader上的三种扩展方法:

第一次从流的当前位置读取到末尾 第二个一次性读取整个流 第三个使用Range 类型来指定您感兴趣的数据子集。
public static class BinaryReaderExtensions 

    public static byte[] ReadBytesToEnd(this BinaryReader binaryReader) 
    
        var length = binaryReader.BaseStream.Length - binaryReader.BaseStream.Position;
        return binaryReader.ReadBytes((int)length);
    
    
    public static byte[] ReadAllBytes(this BinaryReader binaryReader) 
    
        binaryReader.BaseStream.Position = 0;
        return binaryReader.ReadBytes((int)binaryReader.BaseStream.Length);
    

    public static byte[] ReadBytes(this BinaryReader binaryReader, Range range) 

        var (offset, length) = range.GetOffsetAndLength((int)binaryReader.BaseStream.Length);
        binaryReader.BaseStream.Position = offset;
        return binaryReader.ReadBytes(length);      
    

使用它们就变得简单明了...

// 1 - Reads everything in as a byte array
var rawBytes = myBinaryReader.ReadAllBytes();

// 2 - Reads a string, then reads the remaining data as a byte array
var someString = myBinaryReader.ReadString();
var rawBytes = myBinaryReader.ReadBytesToEnd();

// 3 - Uses a range to read the last 44 bytes
var rawBytes = myBinaryReader.ReadBytes(^44..);

【讨论】:

【参考方案3】:

原始答案(阅读下面的更新!)

简单地做:

byte[] allData = read1.ReadBytes(int.MaxValue);

documentation 表示它将读取所有字节,直到到达流的末尾。


更新

虽然这看起来很优雅,并且文档似乎表明这会起作用,但实际的实现(在 .NET 2、3.5 和 4 中检查)为数据,这可能会在 32 位系统上导致 OutOfMemoryException

因此,我会说实际上没有优雅的方式

相反,我会推荐@iano 答案的以下变体。此变体不依赖 .NET 4: 为BinaryReader(或Stream,代码相同)创建一个扩展方法。

public static byte[] ReadAllBytes(this BinaryReader reader)

    const int bufferSize = 4096;
    using (var ms = new MemoryStream())
    
        byte[] buffer = new byte[bufferSize];
        int count;
        while ((count = reader.Read(buffer, 0, buffer.Length)) != 0)
            ms.Write(buffer, 0, count);
        return ms.ToArray();
    
    

【讨论】:

这给了我 .NET 4.0 中的 OutOfMemoryException(使用 LINQPad 进行测试)。实际上,使用 Reflector 对源代码进行反编译表明,ReadBytes 尝试分配一个具有给定计数大小的字节数组:byte[] buffer = new byte[count];. @iano 你是对的。我也反编译了.NET 2.0,也是一样。我将用免责声明更新我的答案。 有人可以向我解释为什么 buffer = new byte[count] 会导致内存不足异常吗?我想了解为什么需要缓冲的基本原理。谢谢 @ShrageSmilowi​​tz 好吧,如果您创建一个包含int.MaxValue 32 位整数的数组,您将分配 8GB 内存……所以您应该使用更小的缓冲区来构建结果! @user626528 早在 2011 年,当 .NET 3.5 占主导地位时,这是最简单的答案。我同意你的看法,现在 iano 的回答要好得多。【参考方案4】:

解决这个问题的另一种方法是使用 C# 扩展方法:

public static class StreamHelpers

   public static byte[] ReadAllBytes(this BinaryReader reader)
   
      // Pre .Net version 4.0
      const int bufferSize = 4096;
      using (var ms = new MemoryStream())
      
        byte[] buffer = new byte[bufferSize];
        int count;
        while ((count = reader.Read(buffer, 0, buffer.Length)) != 0)
            ms.Write(buffer, 0, count);
        return ms.ToArray();
      

      // .Net 4.0 or Newer
      using (var ms = new MemoryStream())
      
         stream.CopyTo(ms);
         return ms.ToArray();
      
   

使用这种方法将允许可重用​​和可读的代码。

【讨论】:

这不会构建 - .NET 4.0 部分下的“流”变量未在任何地方定义。【参考方案5】:

为了将一个流的内容复制到另一个,我已经解决了读取“一些”字节直到到达文件末尾的问题:

private const int READ_BUFFER_SIZE = 1024;
using (BinaryReader reader = new BinaryReader(responseStream))

    using (BinaryWriter writer = new BinaryWriter(File.Open(localPath, FileMode.Create)))
    
        int byteRead = 0;
        do
        
            byte[] buffer = reader.ReadBytes(READ_BUFFER_SIZE);
            byteRead = buffer.Length;
            writer.Write(buffer);
            byteTransfered += byteRead;
         while (byteRead == READ_BUFFER_SIZE);                        
                    

【讨论】:

这对我有用,谢谢。 (您需要删除 'byteTransfered += byteRead;' 行才能按原样运行。)【参考方案6】:

BinaryReader 没有一种简单的方法可以做到这一点。如果您不知道需要提前读取的计数,最好使用 MemoryStream:

public byte[] ReadAllBytes(Stream stream)

    using (var ms = new MemoryStream())
    
        stream.CopyTo(ms);
        return ms.ToArray();
    

为避免在调用ToArray() 时产生额外的副本,您可以改为通过GetBuffer() 返回Position 和缓冲区。

【讨论】:

我同意,这可能是最优雅的答案。不过值得注意的是,Stream.CopyTo 仅在 .NET 4 中可用。 +1。在为我的困境寻找解决方案时,我偶然发现了这个答案。我在一个派生自Stream 的第3 方程序集中的类(我想从中获取所有字节)中遇到问题,但它的Length 属性始终为零。我最初尝试了一种基于扩展方法的方法,但觉得它很笨拙。 我会添加 stream.Position = 0;复制到之前 你如何获得流媒体?讨厌这些不完整的答案 @GustavoBaiocchiCosta yourBinaryReader.BaseStream

以上是关于一种消耗(所有字节)BinaryReader 的优雅方式?的主要内容,如果未能解决你的问题,请参考以下文章

BinaryWriter和BinaryReader用法

原 BinaryWriter和BinaryReader(二进制文件的读写)

BinaryReader.PeekChar()读取了多少位?

使用 BinaryReader 解析 Wave 文件

C# 读写二进制文件

FileUpload转换为字节