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

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

我这样做:

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

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

【问题讨论】:

  • 我认为毕竟这是一种优雅的赌注方式

标签: c# .net stream streamreader binaryreader


【解决方案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
      猜你喜欢
      • 2015-08-28
      • 1970-01-01
      • 2016-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-18
      • 1970-01-01
      相关资源
      最近更新 更多