C #, читать структуры из двоичного файла - PullRequest
6 голосов
/ 11 ноября 2010

Я хочу читать структуры из двоичного файла. В C ++ я бы сделал это так:

stream.read((char*)&someStruct, sizeof(someStruct));

Есть ли подобный способ в C #? BinaryReader работает только для встроенных типов. В .NET 4 есть MemoryMappedViewAccessor. Он предоставляет такие методы, как Read<T>, что, как мне кажется, то, что я хочу, за исключением того, что мне нужно вручную отслеживать, где в файле я хочу прочитать. Есть ли лучший способ?

Ответы [ 5 ]

13 голосов
/ 12 ноября 2010
public static class StreamExtensions
{
    public static T ReadStruct<T>(this Stream stream) where T : struct
    {
        var sz = Marshal.SizeOf(typeof(T));
        var buffer = new byte[sz];
        stream.Read(buffer, 0, sz);
        var pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        var structure = (T) Marshal.PtrToStructure(
            pinnedBuffer.AddrOfPinnedObject(), typeof(T));
        pinnedBuffer.Free();
        return structure;
    }
}

Необходимо убедиться, что ваша структура объявлена ​​с аннотациями [StructLayout] и, возможно, [FieldOffset], чтобы соответствовать двоичному макету в файле

РЕДАКТИРОВАТЬ:

Использование:

SomeStruct s = stream.ReadStruct<SomeStruct>();
2 голосов
/ 13 мая 2015

Вот немного измененная версия кода Джеспера:

public static T? ReadStructure<T>(this Stream stream) where T : struct
{
    if (stream == null)
        return null;

    int size = Marshal.SizeOf(typeof(T));
    byte[] bytes = new byte[size];
    if (stream.Read(bytes, 0, size) != size) // can't build this structure!
        return null;

    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
}

Он успешно обрабатывает случаи EOF и возвращает обнуляемый тип.

2 голосов
/ 12 ноября 2010

Можно сделать что-то похожее в C #, но тогда вам придется применить множество атрибутов к структуре, чтобы вы точно контролировали ее расположение в памяти.По умолчанию JIT-компилятор управляет тем, как элементы структуры размещаются в памяти, что обычно означает, что они переупорядочиваются и дополняются для наиболее эффективной компоновки с учетом скорости и использования памяти.

Самый простой способ - обычно использовать BinaryReaderпрочитать отдельные элементы структуры в файле и поместить значения в свойствах класса, то есть вручную десериализовать данные в экземпляр класса.

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

1 голос
/ 12 ноября 2010

Просто для уточнения ответа Гуффы и Джесперла, вот пример чтения в заголовке файла для файла ASF (WMV / WMA) с использованием в основном того же метода ReadStruct (только не как метод расширения)

MemoryStream ms = new MemoryStream(headerData);
AsfFileHeader asfFileHeader = ReadStruct<AsfFileHeader>(ms);


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
internal struct AsfFileHeader
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
    public byte[] object_id;
    public UInt64 object_size;
    public UInt32 header_object_count;
    public byte r1;
    public byte r2;
}
1 голос
/ 12 ноября 2010

Нет аналогичного способа в C #.Более того, это устаревший способ сериализации из-за его непереносимости.Вместо этого используйте http://www.codeproject.com/KB/cs/objserial.aspx.

...