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

голоса
42

Я пытаюсь читать двоичные данные с помощью 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;
}

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

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

Задан 05/08/2008 в 15:28
источник пользователем
На других языках...                            


7 ответов

голоса
19

Проблема заключается в строке s в вашей структуре. Я обнаружил , что выстраивая типы как байт / короткий / междунар не является проблемой; но когда вам нужно мобилизовывать в сложный тип , такие как строка, вам нужно - структура , чтобы явно имитировать неуправляемый тип. Вы можете сделать это с Attrib 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;
}
Ответил 21/08/2008 d 20:02
источник пользователем

голоса
8

Вот что я использую.
Это успешно работает для меня для чтения Portable Executable формата.
Это общая функция, так что 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;
}
Ответил 02/11/2010 d 03:40
источник пользователем

голоса
5

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

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

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

Ответил 06/05/2010 d 02:04
источник пользователем

голоса
3

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

Encoding.ASCII.GetString()

для строк и

BitConverter.ToInt32()

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

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

Ответил 06/08/2008 d 10:03
источник пользователем

голоса
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 имеет свой собственный формат данных, полностью несовместимый с вашими.

Ответил 05/08/2008 d 16:31
источник пользователем

голоса
0

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

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

Ответил 23/09/2008 d 22:43
источник пользователем

голоса
0

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

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
Ответил 05/08/2008 d 15:56
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more