2008-08-05 21 views
41

Estoy tratando de leer datos binarios usando C#. Tengo toda la información sobre el diseño de los datos en los archivos que quiero leer. Puedo leer los datos "fragmento por fragmento", es decir, obtener los primeros 40 bytes de datos convirtiéndolos en una cadena, obtener los siguientes 40 bytes.Lea el archivo binario en una estructura

Dado que hay al menos tres versiones ligeramente diferentes de los datos, me gustaría leer los datos directamente en una estructura. Simplemente se siente mucho más bien que leyéndola "línea por línea".

He intentado el siguiente enfoque, pero en vano:

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

La corriente es un FileStream abierto de la que he comenzado a leer. Obtengo AccessViolationExceptio n cuando uso Marshal.PtrToStructure.

La secuencia contiene más información de la que estoy tratando de leer porque no estoy interesado en los datos al final del archivo.

La estructura se define como:

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

El código se cambia de ejemplos original para hacer esta pregunta más corto.

¿Cómo leería los datos binarios de un archivo en una estructura?

Respuesta

0

Prueba esto:

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

BinaryFormatter tiene su propio formato para datos binarios, que está bien si está leyendo/escribiendo los datos usted mismo. no es útil si obtiene un archivo de otra fuente. – russau

1

no veo ningún problema con su código.

fuera de mi cabeza, ¿y si intentas hacerlo manualmente? ¿Funciona?

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)); 
... 
... 
... 

también tratar

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

a continuación, utilizar buffer [] en su BinaryReader en lugar de leer los datos de FileStream para ver si usted todavía consigue AccessViolation excepción.

no he tenido suerte con el BinaryFormatter , supongo que tengo que tienen una estructura completa que coincide con el contenido del archivo exactamente.

Eso tiene sentido, BinaryFormatter tiene su propio formato de datos, completamente incompatible con el suyo.

3

No he tenido suerte usando BinaryFormatter, supongo que tengo que tener una estructura completa que coincida exactamente con el contenido del archivo. Me di cuenta de que al final no estaba interesado en gran medida del contenido del archivo de todos modos, así que fui con la solución de la lectura parte de la corriente en un ByteBuffer y luego convertirlo usando

Encoding.ASCII.GetString() 

para cuerdas y

BitConverter.ToInt32() 

para enteros.

Necesitaré poder analizar más el archivo más adelante, pero para esta versión me salí con solo un par de líneas de código.

18

El problema es cadena s en su estructura. Descubrí que los tipos de ordenación como byte/short/int no son un problema; pero cuando necesita llegar a un tipo complejo como una cadena, necesita que su estructura imite explícitamente un tipo no administrado. Puedes hacer esto con MarshalAs attrib.

Para su ejemplo, lo siguiente debe funcionar:

[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

lectura directamente en estructuras es malo - muchos un programa en C ha caído debido a los diferentes ordenamientos bytes, diferentes implementaciones del compilador de campos, embalaje, tamaño de la palabra .......

Lo mejor es serializar y deserializar byte por byte. Usa la construcción en cosas si quieres o simplemente acostúmbrate a BinaryReader.

+6

No estoy de acuerdo, leer directamente en las estructuras a veces es la manera más rápida de obtener sus datos en un objeto utilizable. Si está escribiendo código orientado al rendimiento, esto puede ser muy útil. Sí, debe conocer las alineaciones y el empaque y asegurarse de que cualquier máquina de punto final use el mismo. – Joe

+3

También estoy en desacuerdo. Cuando el rendimiento es clave, o cuando se necesita interoperabilidad binaria en C++/C#, escribir 'struct's simple es el camino a seguir. –

5

Como dijo Ronnie, usaría BinaryReader y leería cada campo individualmente. No puedo encontrar el enlace al artículo con esta información, pero se ha observado que el uso de BinaryReader para leer cada campo individual puede ser más rápido que Marshal.PtrToStruct, si la estructura contiene menos de 30 o 40 campos. Publicaré el enlace al artículo cuando lo encuentre.

enlace del artículo se encuentra en: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

Cuando cálculo de referencias de una gran variedad de estructuras, PtrToStruct gana la mano superior con mayor rapidez, porque se puede pensar en el número de campos como campos * longitud de la matriz.

+2

Estaba leyendo: http://www.codeproject.com/KB/files/fastbinaryfileinput.aspx. ¿Es este el artículo en el que estás pensando? El autor señala: "Encontré que, en aproximadamente 40 campos, los resultados de los tres enfoques eran casi equivalentes, y más allá de eso, los enfoques de lectura en bloques ganaron ventaja". –

+0

¡De hecho lo es! Buen descubrimiento :) – nevelis

8

Esto es lo que estoy usando.
Esto funcionó con éxito para mí para leer Portable Executable Format.
Es una función genérica, por lo que T es su tipo 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; 
}