2010-08-21 13 views
5

Tengo un archivo plano generado por un programa C. Cada registro en el archivo consiste en un encabezado de longitud fija seguido de datos. El encabezado contiene un campo que indica el tamaño de los siguientes datos. Mi objetivo final es escribir un programa C#/.NET para consultar este archivo plano, así que estoy buscando la manera más eficiente de leer el archivo usando C#.¿Manera preferida de analizar un archivo plano binario personalizado?

Tengo problemas para encontrar el .NET equivalente de la línea 7 en el siguiente código. Por lo que puedo decir, tengo que emitir múltiples lecturas (una para cada campo del encabezado usando BinaryReader) y luego emitir una lectura para obtener los datos que siguen al encabezado. Estoy intentando aprender una forma de analizar un registro en dos operaciones de lectura (una lectura para obtener el encabezado de longitud fija y una segunda lectura para obtener los siguientes datos).

Este es el código C que estoy tratando de duplicar el uso de C#/NET:.

struct header header; /* 1-byte aligned structure (48 bytes) */ 
char *data; 

FILE* fp = fopen("flatfile", "r"); 
while (!feof(fp)) 
{ 
    fread(&header, 48, 1, fp); 
    /* Read header.length number of bytes to get the data. */ 
    data = (char*)malloc(header.length); 
    fread(data, header.length, 1, fp); 
    /* Do stuff... */ 
    free(data); 
} 

Esta es la estructura C de la cabecera:

struct header 
{ 
    char id[2]; 
    char toname[12]; 
    char fromname[12]; 
    char routeto[6]; 
    char routefrom[6]; 
    char flag1; 
    char flag2; 
    char flag3; 
    char flag4; 
    char cycl[4]; 
    unsigned short len; 
}; 

se me ha ocurrido con este C# objeto para representar el encabezado C:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi, Size = 48)] 
class RouterHeader 
{ 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] 
    char[] Type; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] 
    char[] To; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] 
    char[] From; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 
    char[] RouteTo; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 
    char[] RouteFrom; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] 
    char[] Flags; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] 
    char[] Cycle; 

    UInt16 Length; 
} 
+0

¿Cómo se ve tu encabezado? –

+1

posible duplicado de [AC# equivalente del archivo de fread de E/o de C] (http://stackoverflow.com/questions/1935851/ac-equivalent-of-cs-fread-file-io) –

+0

He editado la publicación para contener la estructura del encabezado. –

Respuesta

0

El link Hans Passant provided tiene la respuesta. Le daría crédito, pero no estoy seguro de qué hacer ya que publicó un comentario en lugar de una respuesta.

0

Como alternativa, puede intentar usar una estructura tipo unión para crear una cabecera r struct que puede leer de una vez (como una cadena de una longitud adecuada, por ejemplo), pero luego puede hacer referencia a los campos individuales cuando se trata de información de esa estructura.

Puede encontrar más detalles sobre el uso de StructLayouts y FieldOffsets para lograr ese tipo de cosas here.

Hay más discusión sobre la lectura de & escribiendo archivos binarios con C# here. Se sugiere que el uso de BinaryReader para leer en múltiples campos generalmente es más eficiente para pequeños (< 40) cantidad de campos.

2

Bueno, puede usar una llamada a Stream.Read para leer la longitud (aunque debe verificar el valor de retorno para asegurarse de haber leído todo lo que solicitó, puede que no lo obtenga todo de una vez) y luego otra llamada al Stream.Read para obtener los datos en una matriz de bytes (de nuevo, en bucle hasta que haya leído algo). Una vez que todo esté en la memoria, puede seleccionar los bytes apropiados del buffer para crear una instancia de su estructura (o clase).

Personalmente prefiero hacer todo esto explícitamente en lugar de usar StructLayout - este último siempre se siente algo frágil para mí.

0

Le recomiendo que solo escriba el código (una declaración por campo) que lea los campos uno por uno. Es un pequeño código adicional, pero da más flexibilidad. Para empezar, lo libera del requisito de que su estructura de datos en memoria tenga el mismo diseño que el archivo en el disco. Podría ser parte de otra estructura, puede usar String en lugar de char[], por ejemplo.

Considere también: ¿Qué sucede si necesita escribir una versión 2.0, donde se agrega un nuevo campo al final de la estructura? En su ejemplo, necesitaría definir una nueva estructura y quedaría atrapado con ambas definiciones. Si elige el código de lectura/escritura, puede admitir ambos con el mismo código leyendo el nuevo elemento condicionalmente.

0

Mi inclinación sería leer los datos en una matriz, y luego ensamblar el objeto de datos de forma apropiada, usando cambios y agregados para manejar palabras, longwords, etc.Tengo algunas clases de utilidad para manejar ese tipo de cosas.

Cuestiones relacionadas