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





Попробуй это:
using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
Я не вижу проблем с вашим кодом.
просто из головы, а что если попробовать сделать вручную? это работает?
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.
Я не согласен, чтение прямо в структуры иногда является самым быстрым способом превратить ваши данные в полезный объект. Если вы пишете код, ориентированный на производительность, это может быть очень полезно. Да, вы должны знать о выравнивании и упаковке и быть уверенным, что любая конечная машина будет использовать то же самое.
Я тоже не согласен. Когда производительность является ключевым фактором или когда вам нужно двоичное взаимодействие C++ / C#, лучше всего написать простые struct.
Как сказал Ронни, я бы использовал 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 полях результаты трех подходов были почти эквивалентными, а кроме того, подходы блочного чтения одержали верх».
Вот что я использую.
Это сработало для меня успешно при чтении 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;
}
BinaryFormatter имеет собственный формат для двоичных данных - это нормально, если вы сами читаете / записываете данные. бесполезно, если вы получаете файл из другого источника.