Эквивалент C # ввода-вывода fread файла

Может ли кто-нибудь сказать мне, как получить массив байтов в структуру прямым способом в C # .NET версии 2? Подобно знакомому fread, найденному в C, до сих пор я не добился большого успеха в чтении потока байтов и автоматическом заполнении структуры. Я видел некоторые реализации, в которых в управляемом коде присутствует фокус-покус указателя с использованием ключевого слова unsafe.

Взгляните на этот образец:

public unsafe struct foobarStruct{

   /* fields here... */

   public foobarStruct(int nFakeArgs){
      /* Initialize the fields... */
   }

   public foobarStruct(byte[] data) : this(0) {
      unsafe {
         GCHandle hByteData = GCHandle.Alloc(data, GCHandleType.Pinned);
         IntPtr pByteData = hByteData.AddrOfPinnedObject();
         this = (foobarStruct)Marshal.PtrToStructure(pByteData, this.GetType());
         hByteData.Free();
      }
   }
}

Причина, по которой у меня два конструктора в foobarStruct

  • Разве не может быть пустого конструктора.
  • Передайте блок памяти (как массив байтов) в конструктор при создании экземпляра структуры.

Достаточно ли хороша эта реализация или есть более чистый способ добиться этого?

Изменить: я не хочу использовать интерфейс ISerializable или его реализацию. Я пытаюсь прочитать двоичное изображение, чтобы определить используемые поля и определить его данные с помощью структур PE.


person t0mm13b    schedule 20.12.2009    source источник
comment
даже в C очень плохая идея напрямую fread в struct из-за соображений заполнения и выравнивания   -  person Gregory Pakosz    schedule 20.12.2009
comment
Вы не думали об использовании здесь сериализации?   -  person Captain Comic    schedule 20.12.2009
comment
Эта операция должна происходить внутри небезопасного блока, потому что это небезопасно. Структура может содержать элементы, указывающие на ссылочные типы и т. Д. Вы просите взять неизвестные байты диска и поместить их в структуру, которая может содержать указатели на что угодно. Слишком сложно просить фреймворк проверить, что вы пытаетесь сделать, отсюда и небезопасный блок. Вы все еще можете это сделать, но фреймворк должен использовать ваш собственный подход. Сериализация решает основные проблемы за вас, но не подходит для всех сценариев. Я не думаю, что у вас все получится лучше, чем показанный код.   -  person Michael A. McCloskey    schedule 20.12.2009
comment
Спасибо, ребята, за ваш вклад! Счастливого Рождества и Новогодних праздников :)   -  person t0mm13b    schedule 21.12.2009


Ответы (2)


Нет ничего плохого в использовании маршаллера P / Invoke, он небезопасен, и вам не нужно использовать ключевое слово unsafe. Если вы сделаете это неправильно, то получите неверные данные. Это может быть намного проще в использовании, чем явное написание кода десериализации, особенно когда файл содержит строки. Вы не можете использовать BinaryReader.ReadString (), он предполагает, что строка была написана BinaryWriter. Однако убедитесь, что вы объявляете структуру данных с помощью объявления struct, this.GetType () вряд ли сработает хорошо.

Вот общий класс, который заставит его работать для любого объявления структуры:

  class StructureReader<T> where T : struct {
    private byte[] mBuffer;
    public StructureReader() {
      mBuffer = new byte[Marshal.SizeOf(typeof(T))];
    }
    public T Read(System.IO.FileStream fs) {
      int bytes = fs.Read(mBuffer, 0, mBuffer.Length);
      if (bytes == 0) throw new InvalidOperationException("End-of-file reached");
      if (bytes != mBuffer.Length) throw new ArgumentException("File contains bad data");
      T retval;
      GCHandle hdl = GCHandle.Alloc(mBuffer, GCHandleType.Pinned);
      try {
        retval = (T)Marshal.PtrToStructure(hdl.AddrOfPinnedObject(), typeof(T));
      }
      finally {
        hdl.Free();
      }
      return retval;
    }

Пример объявления структуры данных в файле:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct Sample {
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
  public string someString;
}

Вам нужно будет настроить объявление структуры и атрибуты, чтобы они соответствовали данным в файле. Пример кода, который читает файл:

  var data = new List<Sample>();
  var reader = new StructureReader<Sample>();
  using (var stream = new FileStream(@"c:\temp\test.bin", FileMode.Open, FileAccess.Read)) {
    while(stream.Position < stream.Length) {
      data.Add(reader.Read(stream));
    }
  }
person Hans Passant    schedule 20.12.2009

Вероятно, вы захотите использовать BinaryReader, который позволяет читать примитивные типы в двоичной форме.

Создайте MemoryStream из byte[], а затем используйте BinaryReader от этого. Вы должны иметь возможность прочитать структуру и соответствующим образом заполнить свой объект.

person Jeff Foster    schedule 20.12.2009
comment
Я понимаю, что это старый пост, однако те, кто находит, ищут ответ в 2019 году или позже, именно так я и поступил. Я создал методы расширения в BinaryReader, которые обрабатывали проверку и заполняли мои объекты необработанными данными byte []. Если вы используете .net core 2.1 или новее, вы также можете использовать Span ‹T› вместо массива байтов. - person Steven Hoff; 07.02.2019