Читать бинарный файл в структуру

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

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

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

5.08.2008 14:28:57
7 ОТВЕТОВ

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

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
0
5.08.2008 14:56:10
BinaryFormatter имеет свой собственный формат для двоичных данных - это хорошо, если вы сами читаете / записываете данные. бесполезно, если вы получаете файл из другого источника.
russau 26.07.2009 07:11:52

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

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

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.

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

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

3
6.08.2008 09:13:24

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

Encoding.ASCII.GetString()

для строк и

BitConverter.ToInt32()

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

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

3
7.01.2012 00:53:09

Проблема в строке s в вашей структуре. Я обнаружил, что маршалинг типов, таких как 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;
}
30
21.08.2008 19:02:50

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

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

0
23.09.2008 21:43:29
Я не согласен, чтение прямых структур иногда является самым быстрым способом передачи ваших данных в пригодный для использования объект. Если вы пишете ориентированный на производительность код, это может быть очень полезно. Да, вы должны знать о выравнивании и упаковке и быть уверенным, что любой конечный автомат будет использовать то же самое.
Joe 3.02.2012 20:00:30
Я тоже не согласен. Когда производительность является ключевым фактором, или когда вам нужно двоичное взаимодействие C ++ / C #, лучше написать простой structs.
Dmitri Nesteruk 25.03.2012 07:48:04

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

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

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

6
1.09.2012 17:34:05
Я просто читал: codeproject.com/KB/files/fastbinaryfileinput.aspx . Вы думаете об этой статье? Автор отмечает: «Я обнаружил, что примерно на 40 полях результаты для трех подходов были почти эквивалентны, и, кроме того, подходы блочного чтения получили преимущество».
Neal Stublen 10.06.2010 19:39:20

Вот что я использую.
Это успешно сработало для меня для чтения 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;
}
11
31.08.2015 16:08:11