Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: Awraaaauu en 23 Junio 2010, 04:41 am



Título: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Awraaaauu en 23 Junio 2010, 04:41 am
Si alguien ve que lo que digo es una tontería que me corrija y pase de leer el código.

Mi intención aquí era escribir y leer de un archivo binario usando streams. De manera que Datos -> Estructura(a lo buffer) -> archivo.dat y viceversa.

Para saber dónde y desde dónde leer he usado seekg y seekp, multiplicando el número de registro por sizeof(estructura).

El problema es que cuando introduzco un segundo registro me sobrescribe el primero con NULL las veces que sea necesario hasta llenar el hueco.

Ahí va el código:
Código
  1. #include <iostream>
  2. #include <cstring>
  3. #include <cstdio>
  4. #include <fstream>
  5. using namespace std;
  6. //Estrucutra / Buffer
  7. typedef struct {
  8.       char nombre[34];
  9.       int ano;
  10.       short dia;
  11.       short mes;
  12.       } Cumple;
  13. //Prototipos      
  14. void introduccion();      
  15. char ElegirConsulta();
  16. void ConsultaID();
  17. void grabado();
  18. void lectura();
  19. //Variables Globales
  20. char meses[12][11] = {"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"};
  21. long nRegistros = 0;
  22. long nBytes = 0;
  23. int id;
  24. Cumple pers;
  25. //Función Principal
  26. int main() {
  27.    int opt;
  28.    bool bucle = false;
  29.    while(!bucle)      {
  30.    system("cls");
  31.    ifstream fichero("datos.dat", ios::in | ios::binary);                  
  32.    fichero.seekg(0, ios::end); // Colocar el cursor al final del fichero
  33.    nBytes = fichero.tellg(); // Tamaño en bytes
  34.    nRegistros = fichero.tellg()/sizeof(Cumple); // Tamaño en registros
  35.    fichero.close();
  36.    cout << "Acci\242n:" << endl;
  37.    cout << " 1 - Introducir datos" << endl;
  38.    cout << " 2 - Consultar datos" << endl;
  39.    cout << "99 - Salir" << endl;
  40.    cout << "Opci\242n: ";
  41.    cin >> opt;
  42.    switch(opt) {
  43.               case 1:
  44.                 introduccion();
  45.                 break;
  46.               case 2:
  47.                    if(ElegirConsulta() == 'a') ConsultaID();
  48.                    break;
  49.               case 99:
  50.                    cout << "Vete ya CO\245O!!!" << endl;
  51.                    cin.get();
  52.                    cin.get();
  53.                    bucle = true;
  54.                    }
  55.                    }
  56.    return 0;
  57. }
  58. //Función de introducción de datos
  59. void introduccion() {
  60.     bool correcto = false;
  61.     char op;
  62.     cin >> id;
  63.     while(!correcto) {
  64.     system("cls");
  65.     cout << "ID: " << id << endl;
  66.     cout << "Nombre: "; cin >> pers.nombre; cout << endl;
  67.     cout << "A\244o: "; cin >> pers.ano; cout << endl;
  68.     cout << "Mes: "; cin >> pers.mes; cout << endl;
  69.     cout << "D\241a: "; cin >> pers.dia; cout << endl;
  70.     cout << endl << endl;
  71.     cout << "Datos introducidos: " << endl;
  72.     cout << pers.nombre << " naci\242 el " << pers.dia << " de " << meses[pers.mes-1] << " de " << pers.ano << endl;
  73.     cout << "Es esto correcto?(S/n)" << endl;
  74.     cin >> op;
  75.     if(op == 'S' || op == 's') correcto = true;
  76.     }
  77.     grabado();
  78.     }
  79. //Menu de consulta
  80. char ElegirConsulta() {
  81.     char op;
  82.     system("cls");
  83.     cout << "C\242mo quieres consultar?" << endl;
  84.     cout << "a) Por ID" << endl;
  85.     cout << "b) Por nombre" << endl; //Por hacer
  86.     cout << "x) Atr\240s" << endl;
  87.     cout << "Opci\242n: ";
  88.     cin >> op;
  89.     return op;
  90.     }
  91. //Función de Consulta de datos por ID
  92. void ConsultaID() {
  93.     system("cls");
  94.     cout << "Escribe ID: ";
  95.     cin >> id;
  96.     lectura();
  97.     cout << pers.nombre << " naci\242 el " << pers.dia << " de " << meses[pers.mes-1] << " de " << pers.ano << endl;
  98.     cin.get();
  99.     cin.get();
  100.     }
  101. //Función de grabado de datos en fichero binario
  102. void grabado() {
  103.     fstream grabado;
  104.     grabado.open("datos.dat", ios::out | ios::trunc | ios::binary);
  105.     grabado.seekp(id*sizeof(Cumple), ios::beg);
  106.     grabado.clear();
  107.     grabado.write((const char *)&pers, sizeof(Cumple));
  108.     grabado.close();
  109.     }
  110. //Función de lectura de datos de fichero binario
  111. void lectura() {
  112.     fstream lectura;
  113.     lectura.open("datos.dat", ios::in | ios::binary);
  114.     lectura.seekg(id*sizeof(Cumple), ios::beg);
  115.     lectura.read((char *)&pers, sizeof(Cumple));
  116.     lectura.close();
  117.     }
  118.  

¿Alguien sabe qué hago mal?

Por cierto, en WinXP, 32bits, con DevC++


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Littlehorse en 23 Junio 2010, 06:34 am
Bueno lo primero y principal es que deberías no utilizar tanto las variables globales. Hacen el código un poco mas difícil de seguir ya que cualquier función puede modificar su contenido.
En cuanto al problema, el principal esta en la apertura del archivo. Tanto ios::out como ios::out|ios::trunc primero descartan el contenido (crean el archivo solo si no existe, pero descartan el contenido existente en el).

Otra cosa que veo que deberías modificar es el método del calculo de posiciones, es bastante propenso a errores.
Supongamos que no tenes ningún dato, y el ID es 1400, tu archivo quedaría algo así:

1400*sizeof(data)bytes
NULL|NULL|NULL|NULL|| contenido ||


lo cual no es lo mas adecuado y de seguro te traerá problemas como archivos excesivamente grandes, punteros a archivo que no apuntan donde deben, superposición de datos y demás.

No he visto mucho mas, pero también deberías chequear los estados del stream (failbit, badbit, etc) para asegurarte de evitar otros tipos de errores.

Como ultimo, las etiquetas GeSHI utilízalas de esta forma:

C
[code=c]codigo[/code]

C++
[code=cpp]codigo[/code]

Saludos!


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Awraaaauu en 23 Junio 2010, 06:44 am
Muchas gracias por tú respuesta.

Se que el código es una "basurita", pero realmente la única función que tenía era conseguir aprender lo de los binarios, le adorné con lo del calendario para que luego no me quedara un exe inútil.

¿Hay alguna función para lo que busco o tengo que crear un buffer para TOODOO el archivo?


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Littlehorse en 23 Junio 2010, 07:07 am
No no hay ninguna. Si queres escribir los datos en forma aleatoria, basta con poner el puntero en una posición relacionada con algún criterio variante, lo cual es precisamente lo que estas haciendo ahora y teóricamente mal no esta (a pesar que tenga los problemas que ya mencione antes). El problema radica en que en cada apertura descartas el contenido del archivo existente.

Obviamente tenes mil formas de ordenar el archivo, lo ideal en este caso seria que cada estructura este detras de la otra en pos de no perder el acceso aleatorio y obtener un archivo lo mas reducido posible.

No veo la necesidad de crear un buffer para todo el archivo, no te daría ninguna ventaja respecto de lo que estas haciendo y en algún punto incluso te podría traer problemas cuando el tamaño del archivo sea considerablemente grande.

Saludos


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Awraaaauu en 23 Junio 2010, 08:26 am
Mi pregunta es:

¿Cómo evitar que lo descarte?


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Littlehorse en 23 Junio 2010, 17:50 pm
Modificando la apertura del archivo para que no vacié el contenido, busca sobre los métodos de apertura. ::app por ejemplo te podría servir, pero primero tienes que modificar el método de calculo de posiciones.

Saludos


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Awraaaauu en 24 Junio 2010, 00:02 am
Muchas gracias, no conocía ese método. Aprendí de un manual bastante incompleto.


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Littlehorse en 24 Junio 2010, 00:14 am
De nada, para eso estamos  :D. En cuanto a lo del manual, no se cual estarás leyendo pero tenes un post (http://foro.elhacker.net/programacion_cc/librospapers_c_and_c-t296234.0.html) en las chinchetas con varios libros para elegir.

Cualquier cosa ya sabes.

Saludos!


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Awraaaauu en 24 Junio 2010, 03:15 am
Sigue habiendo un problemilla.

::app me sitúa al final del fichero siempre antes de cualquier operación de salida, incluso después de hacer seekp

Bueno, voy a investigar un poco por mi cuenta. Si no consigo nada lo haré sin streams.



Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: nicolas_cof en 24 Junio 2010, 15:20 pm
Awraaaauu, prueba con ios::ate en vez de ios::app, ya que con ::app incluso si se cambia la posicion en el archivo siempre se escribe al final de este.

Salu10.


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Littlehorse en 24 Junio 2010, 19:42 pm
Nico, abriéndolo en ate sucede el mismo problema. Es decir, en la próxima apertura del archivo, el contenido se vacía.


Sigue habiendo un problemilla.

::app me sitúa al final del fichero siempre antes de cualquier operación de salida, incluso después de hacer seekp

Bueno, voy a investigar un poco por mi cuenta. Si no consigo nada lo haré sin streams.


Si, precisamente por eso te dije que el calculo de posiciones lo tendrías que cambiar. Es la forma mas fácil para escribir siempre al final del fichero, en todo caso si luego necesitas sobrescribir una sección moviendo el puntero podes abrir el archivo para input/output.

Código
  1.     fstream grabado;
  2.     grabado.open("datos.dat", ios::out|ios::in| ios::binary);

Pero por supuesto si el archivo no existe esto te dará error, por lo tanto primero tenes que chequear que el archivo exista ya sea abriéndolo para lectura y verificando las salidas:

Citar
On failure, the failbit flag is set (which can be checked with member fail), and depending on the value set with exceptions an exception may be thrown.

o bien utilizando funciones especificas del sistema, o alguna librería multiplataforma.

Saludos!


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: nicolas_cof en 24 Junio 2010, 20:29 pm
Cita de: Littlehorse
Nico, abriéndolo en ate sucede el mismo problema. Es decir, en la próxima apertura del archivo, el contenido se vacía.

Littlehorse, hay una cosa que no entiendo :P, supuestamente ::ate significa append to end, lo cual me lleva a dudar de porque el contenido en una nueva apertura se vacia?

De aca estoy leyendo algo... http://www.cplusplus.com/doc/tutorial/files/

Salu10.


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: Littlehorse en 24 Junio 2010, 21:11 pm
El problema no es ::ate, el problema es usarlo con ::out. Serian los equivalentes de estos modos de fopen:

Código
  1. ios::out|ios::in|ios::ate|ios::binary

La posición inicial si estará al final del archivo. Esto seria el equivalente a "r+b si el archivo existe" en C.

pero si haces esto:

Código
  1. ios::out|ios::ate

El equivalente seria "wb". ios::out implica ios::trunc por lo tanto el contenido se sobrescribe. Al menos que el archivo sea abierto para operaciones simultaneas de lectura y escritura.

Saludos!


Título: Re: Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme
Publicado por: nicolas_cof en 24 Junio 2010, 21:14 pm
Littlehorse, gracias. Siempre se aprende algo nuevo.

Salu10.