Acceso aleatorio a archivos binarios, a ver si alguien puede ayudarme

Iniciado por Awraaaauu, 23 Junio 2010, 04:41 AM

0 Miembros y 1 Visitante están viendo este tema.

Awraaaauu

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 (cpp) [Seleccionar]
#include <iostream>
#include <cstring>
#include <cstdio>
#include <fstream>
using namespace std;
//Estrucutra / Buffer
typedef struct {
      char nombre[34];
      int ano;
      short dia;
      short mes;
      } Cumple;
//Prototipos      
void introduccion();      
char ElegirConsulta();
void ConsultaID();
void grabado();
void lectura();
//Variables Globales
char meses[12][11] = {"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"};
long nRegistros = 0;
long nBytes = 0;
int id;
Cumple pers;
//Función Principal
int main() {
   int opt;
   bool bucle = false;
   while(!bucle)      {
   system("cls");
   ifstream fichero("datos.dat", ios::in | ios::binary);                  
   fichero.seekg(0, ios::end); // Colocar el cursor al final del fichero
   nBytes = fichero.tellg(); // Tamaño en bytes
   nRegistros = fichero.tellg()/sizeof(Cumple); // Tamaño en registros
   fichero.close();
   cout << "Acci\242n:" << endl;
   cout << " 1 - Introducir datos" << endl;
   cout << " 2 - Consultar datos" << endl;
   cout << "99 - Salir" << endl;
   cout << "Opci\242n: ";
   cin >> opt;
   switch(opt) {
              case 1:
                introduccion();
                break;
              case 2:
                   if(ElegirConsulta() == 'a') ConsultaID();
                   break;
              case 99:
                   cout << "Vete ya CO\245O!!!" << endl;
                   cin.get();
                   cin.get();
                   bucle = true;
                   }
                   }
   return 0;
}
//Función de introducción de datos
void introduccion() {
    bool correcto = false;
    char op;
    cin >> id;
    while(!correcto) {
    system("cls");
    cout << "ID: " << id << endl;
    cout << "Nombre: "; cin >> pers.nombre; cout << endl;
    cout << "A\244o: "; cin >> pers.ano; cout << endl;
    cout << "Mes: "; cin >> pers.mes; cout << endl;
    cout << "D\241a: "; cin >> pers.dia; cout << endl;
    cout << endl << endl;
    cout << "Datos introducidos: " << endl;
    cout << pers.nombre << " naci\242 el " << pers.dia << " de " << meses[pers.mes-1] << " de " << pers.ano << endl;
    cout << "Es esto correcto?(S/n)" << endl;
    cin >> op;
    if(op == 'S' || op == 's') correcto = true;
    }
    grabado();
    }
//Menu de consulta
char ElegirConsulta() {
    char op;
    system("cls");
    cout << "C\242mo quieres consultar?" << endl;
    cout << "a) Por ID" << endl;
    cout << "b) Por nombre" << endl; //Por hacer
    cout << "x) Atr\240s" << endl;
    cout << "Opci\242n: ";
    cin >> op;
    return op;
    }
//Función de Consulta de datos por ID
void ConsultaID() {
    system("cls");
    cout << "Escribe ID: ";
    cin >> id;
    lectura();
    cout << pers.nombre << " naci\242 el " << pers.dia << " de " << meses[pers.mes-1] << " de " << pers.ano << endl;
    cin.get();
    cin.get();
    }
//Función de grabado de datos en fichero binario
void grabado() {
    fstream grabado;
    grabado.open("datos.dat", ios::out | ios::trunc | ios::binary);
    grabado.seekp(id*sizeof(Cumple), ios::beg);
    grabado.clear();
    grabado.write((const char *)&pers, sizeof(Cumple));
    grabado.close();
    }
//Función de lectura de datos de fichero binario
void lectura() {
    fstream lectura;
    lectura.open("datos.dat", ios::in | ios::binary);
    lectura.seekg(id*sizeof(Cumple), ios::beg);
    lectura.read((char *)&pers, sizeof(Cumple));
    lectura.close();
    }


¿Alguien sabe qué hago mal?

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

Littlehorse

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!
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Awraaaauu

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?

Littlehorse

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
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Awraaaauu


Littlehorse

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
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Awraaaauu

Muchas gracias, no conocía ese método. Aprendí de un manual bastante incompleto.

Littlehorse

De nada, para eso estamos  :D. En cuanto a lo del manual, no se cual estarás leyendo pero tenes un post en las chinchetas con varios libros para elegir.

Cualquier cosa ya sabes.

Saludos!
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Awraaaauu

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.


nicolas_cof

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.