2008-08-05 18 views
41

Я пытаюсь читать двоичные данные с помощью C#. У меня есть вся информация о макете данных в файлах, которые я хочу прочитать. Я могу читать данные «chunk by chunk», то есть получать первые 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, из которого я начал читать. При использовании Marshal.PtrToStructure я получаю AccessViolationExceptio.

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

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

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

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

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

ответ

0

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

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

BinaryFormatter имеет свой собственный формат для двоичных данных - это нормально, если вы сами читаете/записываете данные. не полезно, если вы получаете файл из другого источника. – russau 2009-07-26 07:11:52

1

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

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

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

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

Encoding.ASCII.GetString() 

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

BitConverter.ToInt32() 

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

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

18

Проблема: string 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; 
} 
0

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

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

+6

Я не согласен с тем, что чтение прямо в структуры иногда является самым быстрым способом получить ваши данные в полезный объект. Если вы пишете ориентированный на производительность код, это может быть очень полезно. Да, вы должны знать о выравниваниях и упаковке и быть уверенными, что любая конечная машина будет использовать их. – Joe 2012-02-03 20:00:30

+3

Я тоже не согласен. Когда производительность является ключевой, или когда вам нужен бинарный C++/C# interop, писать простой `struct` это путь. – 2012-03-25 07:48:04

5

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

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

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

8

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