txt - manejo de archivos en c# pdf
Leer archivo binario en una estructura (7)
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 usar BinaryReader para leer cada campo individual puede ser más rápido que Marshal.PtrToStruct, si la estructura contiene menos de 30-40 campos. Publicaré el enlace al artículo cuando lo encuentre.
El enlace del artículo está en: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C
Al ordenar una matriz de estructuras, PtrToStruct gana la ventaja más rápidamente, porque puede pensar en el recuento de campos como campos * longitud de la matriz.
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 mejor que leerlo "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 secuencia es un FileStream abierto del que he comenzado a leer. Recibo una AccessViolationExceptio
n cuando uso Marshal.PtrToStructure
.
La transmisión contiene más información de la que intento leer, ya que no me interesan 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 cambia del original para acortar esta pregunta.
¿Cómo leería datos binarios de un archivo en una estructura?
El problema es la cadena s en su estructura. Descubrí que los tipos de cálculo de referencias como byte / short / int no son un problema; pero cuando necesita ordenar un tipo complejo, como una cadena, necesita que su estructura imite explícitamente un tipo no administrado. Puede hacer esto con el 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;
}
Esto es lo que estoy usando.
Esto funcionó con éxito para mí al leer el formato ejecutable portátil.
Es una función genérica, por lo que T
es su tipo de 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;
}
Leer directamente en estructuras es malo: muchos programas en C se han caído debido a diferentes ordenaciones de bytes, diferentes implementaciones de compilador de campos, empaque, tamaño de palabra .......
Eres el mejor de serializar y deserializar byte a byte. Use la compilación en cosas si lo desea o simplemente acostúmbrese a BinaryReader.
No tuve suerte con BinaryFormatter, creo 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 parte del contenido del archivo de todos modos, así que decidí leer 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 archivos más adelante, pero para esta versión me salí con un par de líneas de código.
No veo ningún problema con tu código.
justo fuera de mi cabeza, ¿qué pasa 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 buffer [] en su BinaryReader en lugar de leer datos de FileStream para ver si aún obtiene la excepción AccessViolation.
No tuve suerte con BinaryFormatter, creo 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 suyo.
Prueba esto:
using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
StructType aStruct = (StructType)formatter.Deserialize(filestream);
}