Сведения о вопросе

Faridun

16:03, 1st July, 2020

Теги

c#   struct   io   binaryfiles    

Чтение двоичного файла в структуру

Просмотров: 521   Ответов: 7

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

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

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

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, из которого я начал читать. Я получаю AccessViolationExceptio n при использовании 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;
}

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

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



  Сведения об ответе

P_S_S

18:03, 1st July, 2020

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

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

[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;
}


  Сведения об ответе

dumai

18:03, 1st July, 2020

Вот что я использую.
Это успешно сработало для меня при чтении портативного исполняемого формата.
Это универсальная функция, поэтому 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;
}


  Сведения об ответе

repe

18:03, 1st July, 2020

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

Ссылка на статью находится по адресу: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

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


  Сведения об ответе

piter

18:03, 1st July, 2020

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

Encoding.ASCII.GetString()

для струн и

BitConverter.ToInt32()

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

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


  Сведения об ответе

$DOLLAR

18:03, 1st July, 2020

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

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

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();

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

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

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


  Сведения об ответе

PAGE

18:03, 1st July, 2020

Попробовать это:

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


  Сведения об ответе

appple

18:03, 1st July, 2020

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться