Элегантный способ использовать (все байты) BinaryReader?

Есть ли элегантный способ эмулировать метод StreamReader.ReadToEnd с помощью BinaryReader? Возможно, чтобы поместить все байты в массив байтов?

Я сделаю это:

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

... но должен быть лучший способ.


person SamFisher83    schedule 23.12.2011    source источник
comment
я думаю, в конце концов, это самый элегантный способ   -  person luka    schedule 03.01.2020


Ответы (6)


Оригинальный ответ (читайте обновление ниже!)

Просто сделайте:

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

В документации говорится, что он будет читать все байты пока не будет достигнут конец потока.


Обновлять

Хотя это кажется элегантным, и в документации указано, что это будет работать, фактическая реализация (проверенная в .NET 2, 3.5 и 4) выделяет для данных полноразмерный массив байтов, который вероятно вызовет OutOfMemoryException в 32-битной системе.

Поэтому я бы сказал, что на самом деле элегантного способа не существует.

Вместо этого я бы рекомендовал следующий вариант ответа @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();
    }
    
}
person Scott Rippey    schedule 23.12.2011
comment
Это дает мне исключение OutOfMemoryException в .NET 4.0 (тестирование с помощью LINQPad). Действительно, декомпиляция исходного кода с помощью Reflector показывает, что ReadBytes пытается выделить массив байтов с размером заданного числа: byte[] buffer = new byte[count];. - person iano; 06.04.2012
comment
@iano Ты прав. Я также декомпилировал .NET 2.0, и это то же самое. Я собираюсь обновить свой ответ с отказом от ответственности. - person Scott Rippey; 09.04.2012
comment
Может ли кто-нибудь объяснить мне основы, почему buffer = new byte[count] может вызвать исключение нехватки памяти? Я хотел бы понять основы буферизации, зачем это нужно. Спасибо - person Shrage Smilowitz; 10.12.2014
comment
@ShrageSmilowitz Что ж, если вы создадите массив, содержащий int.MaxValue 32-битных целых чисел, вы будете выделять 8 ГБ памяти ... вот почему вы должны строить результаты, используя меньшие буферы! - person Scott Rippey; 12.12.2014
comment
Вторая попытка тоже плоха, она не короткая и не элегантная. Здесь есть гораздо лучшие ответы. - person user626528; 14.02.2015
comment
@ user626528 Еще в 2011 году, когда доминировал .NET 3.5, это был самый простой ответ. И я согласен с вами, ответ Иано в наши дни намного лучше. - person Scott Rippey; 15.02.2015
comment
Действительно ли это лучше, чем подход из вопроса? Строка read1.ReadBytes((int)read1.BaseStream.Length); кажется не такой уж плохой, чтобы оправдать метод расширения. Пока Microsoft не добавляет ReadToEnd в BinaryReader (который присутствует в StreamReader), я бы придерживался однострочника. - person LRMAAX; 23.06.2020

Не существует простого способа сделать это с помощью BinaryReader. Если вы не знаете количество, которое вам нужно прочитать заранее, лучше использовать MemoryStream:

public byte[] ReadAllBytes(Stream stream)
{
    using (var ms = new MemoryStream())
    {
        stream.CopyTo(ms);
        return ms.ToArray();
    }
}

Чтобы избежать дополнительной копии при вызове ToArray(), вместо этого вы можете вернуть Position и буфер через GetBuffer().

person iano    schedule 06.04.2012
comment
Я согласен, это, наверное, самый элегантный ответ. Однако стоит отметить, что Stream.CopyTo доступен только в .NET 4. - person Scott Rippey; 09.04.2012
comment
+1. Я наткнулся на этот ответ, когда искал решение своих проблем. У меня была проблема с классом в сторонней сборке (от которой я хотел получить все байты), производным от Stream, но его свойство Length всегда было равно нулю. Сначала я попробовал подход, основанный на методах расширения, но он показался мне громоздким. - person Wai Ha Lee; 04.03.2015
comment
Я бы добавил stream.Position = 0; перед копированием - person Ivan Sokalskiy; 01.04.2016
comment
как получить стрим? Ненавижу эти неполные ответы - person Gustavo Baiocchi Costa; 13.08.2018
comment
@GustavoBaiocchiCosta yourBinaryReader.BaseStream - person jtate; 05.11.2018
comment
Взгляните на мою только что опубликованную версию. Не уверен, что binaryReader.BaseStream.Length был доступен тогда, но теперь мы можем использовать его без необходимости создавать новый MemoryStream! :) - person Mark A. Donohoe; 18.01.2021

Чтобы скопировать содержимое потока в другой, я решил прочитать «некоторые» байты, пока не будет достигнут конец файла:

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);                        
    }                
}
person Seraphim's    schedule 16.05.2014

Другой подход к этой проблеме заключается в использовании методов расширения 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();
      }
   }
}

Использование этого подхода позволит использовать как повторно используемый, так и читаемый код.

person mageos    schedule 29.05.2014
comment
Это не строится - переменная потока в разделе .NET 4.0 нигде не определена. - person BJ Myers; 25.09.2014

Я использую это, которое использует базовое свойство 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..);

person Mark A. Donohoe    schedule 18.01.2021

Возникла та же проблема.
Сначала получите размер файла с помощью FileInfo.Length.
Затем создайте массив байтов и установите для него значение BinaryReader.ReadBytes(FileInfo.Length). например

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