Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Leer Ficheros Binarios En Estructuras (c#)

Iniciado por Lord Trancos 2, 04 de Agosto de 2005, 03:58:08 AM

« anterior - próximo »

Lord Trancos 2

 Estoy intentado crear una clase para leer ficheros binarios (formato propio) con informacion sobre geometria 3D (tri-mesh).


public class clsGeom
{
// Consts --------------------------------------
public const int C_GEOM_ID  = 0x4D4F4547;
public const int C_GEOM_VER = 0x1;

public const int C_GEOM_FLAG_HASNORMALS   = 0x1;
public const int C_GEOM_FLAG_32BITINDICES = 0x2;

public const string C_GEOM_EXT = ".geo";

// Structures ----------------------------------
public struct TGEOM_Header
{
 public int id;
 public int ver;
 public int nSubsets; // num subsets
 public int nFaces;   // number of faces
 public int nVtx;     // number of vertices
 public int nTC;      // number of texture coordinates
 public int nColors;  // number of colors
 public int flags;    // bit 0 - has normals
 //     1 - use 32 bit indices
 //     2..31 - not used yet
 public int reserved; // must be 0
}

/* ... */

public struct TGEOM_3S
{
 public float x;
 public float y;
 public float z;
}


/* ... */

// Properties ----------------------------------
public TGEOM_Header header;

// Methods -------------------------------------
public void LoadFromFile(string _fn)
{
 // Create the reader for data.
 FileStream _fs;
 int moco;

 // open file
 _fs = new FileStream(_fn, FileMode.Open, FileAccess.Read);
 BinaryReader _r = new BinaryReader(_fs);

 // read header
 header.id       = _r.ReadInt32();
 header.ver      = _r.ReadInt32();
 header.nSubsets = _r.ReadInt32();
 header.nFaces   = _r.ReadInt32();
 header.nVtx     = _r.ReadInt32();
 header.nTC      = _r.ReadInt32();
 header.nColors  = _r.ReadInt32();
 header.flags    = _r.ReadInt32();
 header.reserved = _r.ReadInt32();

 /* ... */

 // close
 _r.Close();
}

/* ... */

}


El problema lo tengo al leer la cabecera (por ejemplo). Y es que me parece muy cutre leer dato a dato, ademas de que me huelo que tiene que ser lento de c*j*nes. Sin embargo, las webs que he encontrado que hablan del tema (1) (2), usan código unsafe y por ahora quiero evitar usarlo (por lo de no coger "malos" habitos desde el principio).

Lo mismo me pasa cuando voy a leer el array de vertices (un array de elementos TGEOM_3S) .... ¿como se supone que tengo que leerlo?

En fins,.. me quitan los punteros, el SizeOf,... el reverso tenebroso del unsafe me llama  :ph34r:
on los años y mucho esfuerzo he llegado a atesorar una ignorancia total sobre casi todas las cosas.
Gate to Avalon (mi Blog)

zupervaca

 no te preocupes, la lectura del fichero tiene cache y va a toda leche, yo para leer una estructura lo que suelo hacer es una funcion estatica en la propia estructura que pasandole un stream me devuelve una nueva creada con los datos leidos

saludos

editado: tambien si quieres puedes hacer un constructor pasandole el stream, cuestion de gustos

Vicente

 Hola,

lo mismo te vale también (si lo que lees y escribes es serializable) usar un BinaryFormatter para escribir y leer con Serialize y Deserialize. Un saludo!

Vicente



Lord Trancos 2

Cita de: "zupervaca"no te preocupes, la lectura del fichero tiene cache y va a toda leche, yo para leer una estructura lo que suelo hacer es una funcion estatica en la propia estructura que pasandole un stream me devuelve una nueva creada con los datos leidos
No si eso ya me lo imaginaba.

Pero imaginate que tengo que leer un archivo con 1000 vertices.
Si cada vertice tiene solo 3 componentes (X,Y,Z), eso son 3000 asignaciones.


 for idx = 0 to header.vertex.count-1 do
 {
   buffer[idx].x = _r.ReadFloat();
   buffer[idx].y = _r.ReadFloat();
   buffer[idx].z = _r.ReadFloat();
 }


Acostumbrado a leer todo el buffer de una tacada y meterlo en un puntero... pues como que me parece cutre y seguro que es mucho mas lento.

Tendre que investigar lo que comenta vicente.
on los años y mucho esfuerzo he llegado a atesorar una ignorancia total sobre casi todas las cosas.
Gate to Avalon (mi Blog)

TheAzazel

 Ahora es cuando llego yo y meto la puntilla de los punteros del C jejeje :P

despues de los post anteriores no he podido resistirme....

es broma eh? no os lo tomeis a mal  :rolleyes:


zupervaca

 
CitarPero imaginate que tengo que leer un archivo con 1000 vertices.
Si cada vertice tiene solo 3 componentes (X,Y,Z), eso son 3000 asignaciones
ya eso es cierto, pero es la unica manera de hacer codigo seguro que es lo que se busca con c-sharp, ademas ten seguro que aunque puedas leer varios vertices al mismo tiempo nadie te asegura que las estructuras de los vertices esten alineadas a 1 byte con lo que podrias llegar a perder datos, solo en c++ se puede cambiar esta alineacion, el serialize no te librara de hacer las asignaciones individuales a cada valor del vertice, tu otra solucion es usar unsafe, pero no creo que sea bueno acostumbrarse a algo que puede que en un futuro se elimine.

saludos

editado: se me olvido poner un codigo de ayuda para leer los vertices, teoricamente el c-sharp deberia corregir todo para que siempre funcionara, pero es algo experimental, ademas de que los datos del fichero de vertices de un md2 son de un byte
               VertexMD2[] vertexs = new VertexMD2[head.NumVertices];
               byte[] datos = new byte[head.NumVertices * 5];
               file.Read(datos, 0, head.NumVertices * 5);
               datos.CopyTo(vertexs, 0);
               return vertexs;

asi puedes leerlos todos al mismo tiempo, pero ya te digo que los vertices en el fichero de un md2 son de un byte con lo que la alineacion no importa, el "* 5" es que no se puede usar sizeof si no es unsafe  ;)  

zupervaca

 juaz acabo de darme cuenta que es x,y,z,lightNormalIndex con lo que es *4 y no *5  :lol:  

ethernet

 Desde que descubrí la serialización no he vuelto a hacer loaders... basta con hacer:

Object mi_objeto;
mi_objeto.serialize("bleh.mu");

y punto. No tiene C# implementada la serialización ?

Una cosa que me ha hecho gracia que ha dicho zupervaca sobre la lectura cacheada, que supongo que habrá querido decir buffereada. Que yo recuerde no he visto ninguna implementación de lectura de ficheros que no la lleve (incluso FILE y sus fread, etc).

Vicente

 Si que tiene serialización. Para marcar algo como serializable se usa el atributo [Serializable]. Luego si tienes un array de vértices lo serializas y ya lo tienes guardado de una tacada. Para leerlo lo deserializas y de otra tacada. Sencillo sencillo (supongo que las estructuras son serializables también, que no he probado...).

Para serializar se usa el BinaryFormatter. Pego un poco de código de VB.NET (es un clone usando serializable). Debería ser fácil de entender:


Public Overridable Function Clone() As Object Implements ICloneable.Clone
       Dim formateador As BinaryFormatter
       Dim memoria As MemoryStream

       formateador = New BinaryFormatter
       memoria = New MemoryStream

       'Nos serializamos y volvemos a poner el puntero de lectura/escritura al principio
       formateador.Serialize(memoria, Me)
       memoria.Seek(0, IO.SeekOrigin.Begin)

       'Nos desserializamos para conseguir la copia
       Return formateador.Deserialize(memoria)
   End Function

Vicente

 Hola,

Otra cosa: si quieres hacer una enumeracion de las estilo C/C++ esas que cada valor es una potencia de dos para poder usar el OR, puedes usar el atributo [Flags] (o [Flag] no recuerdo) en la enumeración y te lo hace solo. Algo como:


[Flags]
public enum CosasRara
{
   C_GEOM_FLAG_HASNORMALS,
   C_GEOM_FLAG_32BITINDICES
}



Un saludo!

Vicente

zupervaca

 Una cosa que me ha hecho gracia que ha dicho zupervaca sobre la lectura cacheada, que supongo que habrá querido decir buffereada. Que yo recuerde no he visto ninguna implementación de lectura de ficheros que no la lleve (incluso FILE y sus fread, etc).

pues no entiendo el motivo de por que te ha hecho gracia, un cache es un buffer, no se si sabras que leer 64kb en disco es lo mismo que leer 1byte, con lo que cuando lees un byte el sistema de archivos lee 64kb y si lees continuo sin usar posicionamientos aprovechas esa cache, solo queria decir eso, el serialize de esa clase object lo que hace es un read sobre toda la estructura y me imagino que sera de c++, pero aqui se habla de c-sharp y no c++, en c++ todos sabemos leer una estructura del tiron

saludos

editado: si vas a usar estructuras para probar el serialize que nos comenta vicente ten cuidado con ellas, son muy peligrosas, ya que para pasarlas entre funciones debes usar ref o te hara una copia enterita

ethernet

Cita de: "zupervaca"
pues no entiendo el motivo de por que te ha hecho gracia, un cache es un buffer, no se si sabras que leer 64kb en disco es lo mismo que leer 1byte, con lo que cuando lees un byte el sistema de archivos lee 64kb y si lees continuo sin usar posicionamientos aprovechas esa cache, solo queria decir eso, el serialize de esa clase object lo que hace es un read sobre toda la estructura y me imagino que sera de c++, pero aqui se habla de c-sharp y no c++, en c++ todos sabemos leer una estructura del tiron

saludos
Vicente ha explicado bien lo que yo decía de serialize que es lo que preguntaba, supongo que no lo habrás leído.

Me hace gracia porque a dado la impresión (__a mi__) que lo dices como una caracteristica novedosa de C#, de ahí el comentario, que iba en un tono desenfadado, no con la intentción de mofarme de nadie (que hay que aclararlo todo).

En cuanto a la caché, estamos de acuerdo, una caché puede ser un buffer, pero es igual que si dices que un ferrari son 4 ruedas y un chasis :). Con respecto a los 64 kb, pues no sé, dependerá de la arquitectura y de miles de cosas, pero es obvio que cuando vas a disco, no vas solo a por unos bytes. Todos conocemos el principio de localidad de referencia creo yo.

Con respecto a lo que dices de la clase object, si te refieres a la que he puesto yo, era un ejemplo, no me rerería a ningún lenguaje ni ninguna clase en concreto, era una especie de pseudocódigo para ilustrar mi pregunta y a la vez mostrar el uso que le doy yo a serialize.


zupervaca

 veamos el problema que tiene lord no es leer una estructura del tiron, es leer muchas a la vez como se hace con el fread de toda la vida, eso con c-sharp en principio no se podra hacer por motivos de estabilidad y seguridad, cuando creas un array de vertices, solo has creado un array de punteros a vertice, deberias de recorrerlos todos y crearlos, si creas un array de bytes (que es como se hace en c++) no podras realizar la conversion de byte[] a Vertice[] ya que el jit te dira nanai de la china, si quieres leerlo todo del tiron debes usar unsafe, si quieres ir a la tendencia de c-sharp lee dato a dato, no obstante prueba el serialize que nos indica vicente, pero deberas realizar las asignanciones igual

saludos

ethernet

 En este caso no me refería a caraterísticas concretos de C#, las que desconozco, quería centrarme en que para formatos propios de datos, las típicos de guardar partida o similares, es muy interesante el concepto de serialize, en el cual tu guardas el estado de tu objeto independientemente de cual sea el objeto, y lo recuperas sin preocuparte absolutamente de nada. Para el programador es mucho más cómodo usar eso que programarte tú el exporter/importer.

En este caso puede que no sea el más adecuado ya que puede que ese fichero de mesh lo use en un importador de un programa de modelado y el formato en el que lo guarde el serializer no sea el más sencillo y funcional a la hora de leerlo y, por supuesto, de exportarlo.

Vicente

 Hola,

Si List o ArrayList o un array son serializables no hay que asignar nada: tu metes el array a capon en el fichero y lo lees a capón del fichero ;) Si serializas los vértices uno a uno, pues los desserializas (o como leches se escriba) uno a uno, pero si es el array entero, pues ya está ;) (como lo haga por debajo ni idea).

Respecto a lo que dice ethernet tiene razón, si es un formato que luego tiene que leer otro programa pues chungo usar el serialize (aunque puedes hacerte tu propio serializador si quieres, .NET trae también además del binario el de XML, que seguramente será más humano de intentar parsear desde fuera).

Un saludo!

Vicente






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.