Прочитать двоичный файл в структуру

Я пытаюсь читать двоичные данные с помощью C#. У меня есть вся информация о расположении данных в файлах, которые я хочу прочитать. Я могу читать данные «по частям», т.е. получать первые 40 байтов данных, преобразовывая их в строку, и получаю следующие 40 байтов.

Поскольку существует как минимум три немного разных версии данных, я хотел бы прочитать данные непосредственно в структуре. Это кажется намного более правильным, чем читать его «строка за строкой».

Я пробовал следующий подход, но безрезультатно:

StructType aStruct;
int count = Marshal.SizeOf(typeof(StructType));
byte[] readBuffer = new byte[count];
BinaryReader reader = new BinaryReader(stream);
readBuffer = reader.ReadBytes(count);
GCHandle handle = GCHandle.Alloc(readBuffer, GCHandleType.Pinned);
aStruct = (StructType) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(StructType));
handle.Free();

Поток - это открытый FileStream, из которого я начал читать. Я получаю AccessViolationException при использовании Marshal.PtrToStructure.

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

Структура определяется как:

[StructLayout(LayoutKind.Explicit)]
struct StructType
{
    [FieldOffset(0)]
    public string FileDate;
    [FieldOffset(8)]
    public string FileTime;
    [FieldOffset(16)]
    public int Id1;
    [FieldOffset(20)]
    public string Id2;
}

Код примеров изменен с оригинала, чтобы сделать этот вопрос короче.

Как мне прочитать двоичные данные из файла в структуру?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
57
0
67 580
7

Ответы 7

Попробуй это:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}

BinaryFormatter имеет собственный формат для двоичных данных - это нормально, если вы сами читаете / записываете данные. бесполезно, если вы получаете файл из другого источника.

russau 26.07.2009 11:11

Я не вижу проблем с вашим кодом.

просто из головы, а что если попробовать сделать вручную? это работает?

BinaryReader reader = new BinaryReader(stream);
StructType o = new StructType();
o.FileDate = Encoding.ASCII.GetString(reader.ReadBytes(8));
o.FileTime = Encoding.ASCII.GetString(reader.ReadBytes(8));
...
...
...

также попробуйте

StructType o = new StructType();
byte[] buffer = new byte[Marshal.SizeOf(typeof(StructType))];
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
Marshal.StructureToPtr(o, handle.AddrOfPinnedObject(), false);
handle.Free();

затем используйте буфер [] в вашем BinaryReader вместо чтения данных из FileStream, чтобы увидеть, получаете ли вы по-прежнему исключение AccessViolation.

I had no luck using the BinaryFormatter, I guess I have to have a complete struct that matches the content of the file exactly.

В этом есть смысл, BinaryFormatter имеет собственный формат данных, полностью несовместимый с вашим.

Мне не повезло с использованием BinaryFormatter, я думаю, мне нужна полная структура, которая точно соответствует содержимому файла. Я понял, что, в конце концов, меня все равно не интересовало очень большая часть содержимого файла, поэтому я пошел с решением чтения части потока в байтовый буфер, а затем преобразовал его с помощью

Encoding.ASCII.GetString()

для струнных и

BitConverter.ToInt32()

для целых чисел.

Позже мне нужно будет разобрать больше файла, но для этой версии мне хватило пары строк кода.

Проблема в нить в вашей структуре. Я обнаружил, что такие типы маршалинга, как byte / short / int, не являются проблемой; но когда вам нужно маршалировать в сложный тип, такой как строка, вам нужно, чтобы ваша структура явно имитировала неуправляемый тип. Это можно сделать с помощью атрибута MarshalAs.

В вашем примере должно работать следующее:

[StructLayout(LayoutKind.Explicit)]
struct StructType
{
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string FileDate;

    [FieldOffset(8)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string FileTime;

    [FieldOffset(16)]
    public int Id1;

    [FieldOffset(20)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 66)] //Or however long Id2 is.
    public string Id2;
}

Чтение прямо в структуры - зло - многие программы на C не работают из-за разного порядка байтов, разных реализаций полей, упаковки, размера слова .......

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

Я не согласен, чтение прямо в структуры иногда является самым быстрым способом превратить ваши данные в полезный объект. Если вы пишете код, ориентированный на производительность, это может быть очень полезно. Да, вы должны знать о выравнивании и упаковке и быть уверенным, что любая конечная машина будет использовать то же самое.

Joe 04.02.2012 00:00

Я тоже не согласен. Когда производительность является ключевым фактором или когда вам нужно двоичное взаимодействие C++ / C#, лучше всего написать простые struct.

Dmitri Nesteruk 25.03.2012 11:48

Как сказал Ронни, я бы использовал BinaryReader и прочитал каждое поле отдельно. Я не могу найти ссылку на статью с этой информацией, но было замечено, что использование BinaryReader для чтения каждого отдельного поля может быть быстрее, чем Marshal.PtrToStruct, если структура содержит менее 30-40 полей или около того. Ссылку на статью выложу, когда найду.

Ссылка на статью: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

При маршалинге массива структур PtrToStruct быстрее берет верх, потому что вы можете рассматривать количество полей как поля * длина массива.

Я как раз читал: codeproject.com/KB/files/fastbinaryfileinput.aspx. Вы думаете об этой статье? Автор отмечает: «Я обнаружил, что примерно в 40 полях результаты трех подходов были почти эквивалентными, а кроме того, подходы блочного чтения одержали верх».

Neal Stublen 10.06.2010 23:39

Вот что я использую.
Это сработало для меня успешно при чтении Portable Executable Format.
Это общая функция, поэтому T - это ваш тип struct.

public static T ByteToType<T>(BinaryReader reader)
{
    byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T)));

    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();

    return theStructure;
}

Другие вопросы по теме