Lea el archivo binario en una estructura

votos
42

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, obteniendo los primeros 40 bytes de datos convirtiéndolos en una cadena, obtengo los siguientes 40 bytes.

Como 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 fue 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 transmisión es un FileStream abierto desde el que comencé a leer. Obtengo un AccessViolationException cuando lo uso Marshal.PtrToStructure.

La secuencia contiene más información de la que intento leer, ya que 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 de ejemplos se cambió del original para acortar esta pregunta.

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

Publicado el 05/08/2008 a las 15:28
fuente por usuario
En otros idiomas...                            


7 respuestas

votos
19

El problema es la 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 debería 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;
}
Respondida el 21/08/2008 a las 20:02
fuente por usuario

votos
8

Esto es lo que estoy utilizando.
Esto funcionó con éxito para mí para leer formato ejecutable portátil.
Es una función genérica, por lo que Tes su structtipo.

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;
}
Respondida el 02/11/2010 a las 03:40
fuente por usuario

votos
5

Como dijo Ronnie, que haría uso de BinaryReader y leí cada campo individual. 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-40 o más campos. Voy a publicar el enlace al artículo cuando lo encuentro.

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

Cuando el cálculo de referencias 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.

Respondida el 06/05/2010 a las 02:04
fuente por usuario

votos
3

No tuve 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 muy interesado en el contenido del archivo, así que decidí leer la parte de la transmisión en un bytebuffer y luego convertirlo usando

Encoding.ASCII.GetString()

para cuerdas y

BitConverter.ToInt32()

para los enteros.

Tendré que 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.

Respondida el 06/08/2008 a las 10:03
fuente por usuario

votos
1

No veo ningún problema con tu código.

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

intenta también

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

luego use el búfer [] en su BinaryReader en lugar de leer datos de FileStream para ver si aún obtiene la excepción AccessViolation.

No tuve suerte usando BinaryFormatter, supongo que tengo que tener una estructura completa que coincida exactamente con el contenido del archivo.

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

Respondida el 05/08/2008 a las 16:31
fuente por usuario

votos
0

Leer directamente en las estructuras es malo - muchos programas de C se han caído debido a diferentes ordenamientos de bytes, diferentes implementaciones de campos de compiladores, empaquetamiento, tamaño de palabra .......

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

Respondida el 23/09/2008 a las 22:43
fuente por usuario

votos
0

Prueba esto:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
Respondida el 05/08/2008 a las 15:56
fuente por usuario

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