2010-11-11 23 views
6

Quiero leer las estructuras del binario. En C++ lo haría así:C#, lea las estructuras del archivo binario

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

¿Hay una manera similar en C#? El BinaryReader solo funciona para tipos incorporados. En .NET 4, hay un MemoryMappedViewAccessor. Proporciona métodos como Read<T> que parece ser lo que quiero, excepto que tengo que hacer un seguimiento de la ubicación del archivo que quiero leer manualmente. ¿Hay una manera mejor?

+2

que depende de la forma en que fueron escritos – Andrey

+0

http://www.codeproject.com/KB/cs/objserial.aspx –

+0

http://code.google.com/p/protobuf-net/ – digEmAll

Respuesta

2

Es posible hacer algo similar en C#, pero entonces tendría que aplicar una gran cantidad de atributos a una estructura para que pueda controlar exactamente cómo se presenta en la memoria. De forma predeterminada, el compilador JIT controla cómo se distribuyen los miembros de la estructura en la memoria, lo que generalmente significa que se reorganizan y se rellenan para el diseño más eficiente, teniendo en cuenta la velocidad y el uso de la memoria.

La manera más simple es usualmente usar el BinaryReader para leer los miembros separados de la estructura en el archivo, y poner los valores en propiedades en una clase, es decir, deserializar manualmente los datos en una instancia de clase.

Normalmente, leer el archivo que es el cuello de la botella en esta operación, por lo que la pequeña sobrecarga de leer los miembros por separado no afecta notablemente el rendimiento.

+0

Suena razonable. El rendimiento no es el problema principal aquí, solo pensé que era un poco incómodo. –

+0

Pensando en ello un poco más, no quiero usar un bucle, solo para leer una matriz de algo. –

+0

@B_old: es mucho más fácil escribir las pocas líneas de código para leer el valor de a una por vez, que obtener los atributos correctos para todos los miembros de una estructura, de modo que quede garantizado que se colocará exactamente en la memoria como el archivo está dispuesto. No te escapará del uso de un bucle de alguna forma, sea cual sea la solución que elijas. – Guffa

1

No hay una manera similar en C#. Además, esta es una forma obsoleta de serialización debido a su no portabilidad. Use http://www.codeproject.com/KB/cs/objserial.aspx en su lugar.

+0

El vínculo se rompe – M2X

+0

¿En qué casos no es portátil? Estoy usando ese código de C++ para leer los mismos datos tanto en x86 como en x64 y parece funcionar bien. –

+0

Si escribe los datos en una plataforma (x86, por ejemplo) y lee en otra (64), entonces puede tener problemas. –

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

Usted necesita asegurarse de que su estructura se declara con [StructLayout] y posiblemente [FieldOffset] anotaciones para que coincida con el diseño binario en el archivo

EDIT:

Uso:

SomeStruct s = stream.ReadStruct<SomeStruct>(); 
+0

+1, pero no todas las estructuras se pueden deserializar de esta manera. – arbiter

+0

@jesperll: Esa es una muy mala idea, especialmente si la estructura no es plana. si hay punteros en cualquier parte de la estructura, esa estructura/clase a la que se hace referencia no se escribirá en la salida. Peor aún, cuando se vuelva a leer, apuntará a un espacio de memoria no válido. – casperOne

+0

Es cierto. Te metes en problemas si tienes cosas como arreglos en tu estructura, ya que no son tipos de valor –

1

Solo para elaborar sobre la respuesta de Guffa y jesperll, aquí una muestra sobre la lectura en el encabezado del archivo para un archivo ASF (WMV/WMA) utilizando básicamente el mismo ReadStruct método (pero no tan método de extensión)

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

Aquí es una versión ligeramente modificada del código de Jesper:

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(); 
    } 
} 

Maneja casos EOF éxito, ya que devuelve un tipo anulable.

Cuestiones relacionadas