PDA

Ver la Versión Completa : ofstream o rename fallan


aguml
08-12-2016, 20:07:40
Bueno la verdad es que no se me ocurria otro titulo. El caso es que estoy liado siguiendo aprendiendo cosas y estoy creando una mini clase la cual hace cosas basicas con archivos secuenciales pero el metodo Delete de mi clase me da problemas. Este es el codigo:
int DatosPersonales::Delete(int id){
int retval = -1;
unsigned char *block;
long size_block_beg,size_block_end;
streampos pos;

if(filename != ""){
//Busco el ID a borrar del archivo
pos = (SearchFirst(id,NULL,false) * sizeof(DATOS)) - sizeof(DATOS);

if(pos >= 0){ //Si existe...
//Abro el archivo para leer de el
ifstream f_in( filename.c_str(), ios::binary );
if(!f_in.is_open()){
cout << "No se pudo crear el archivo " << filename << endl;
return -2;
}
//Obtengo el tamaño de los bloques anterior y posterior al dato del id elegido
f_in.seekg( 0, ios::end );
size_block_end = f_in.tellg() - pos - sizeof(DATOS);
f_in.seekg( 0, ios::beg );
size_block_beg = f_in.tellg() + pos;

//Creo un archivo auxiliar donde copiaré todo menos el dato del id elegido
ofstream f_out( "aux.dat",ios::binary );
if(!f_out.is_open()){
cout << "No se pudo crear el archivo auxiliar." << endl;
return -2;
}
//Si el tamaño del bloque anterior es mayor que 0 es porque hay datos antes y tengo
//que meterlos en el archivo auxiliar
if(size_block_beg > 0){
block = new unsigned char[size_block_beg];
f_in.read(block,size_block_beg);
f_out.write(block,size_block_beg);
delete[] block;
}
//Si el tamaño del bloque posterior es mayor que 0 es porque hay datos despues y tengo
//que meterlos en el archivo auxiliar
if(size_block_end > 0){
f_in.seekg( sizeof(DATOS), ios::cur );
block = new unsigned char[size_block_end];
f_in.read(block,size_block_end);
f_out.write(block,size_block_end);
delete[] block;
}
//Cierro ambos archivos
f_in.close();
f_out.close();
//Elimino el archivo original y renombro el auxiliar al nombre del original
remove(string("_" + filename).c_str());
rename(filename.c_str(), string("_" + filename).c_str());
rename("aux.dat",filename.c_str());
retval=0;
}
}else{
retval=-3;
}
return retval;
}
No me da ningun aviso de que no se haya abierto bien f_out ni da ningun tipo de excepcion con el metodo read de este pero el archivo no aparece en el directorio del proyecto y a la hora de ejecutarse el rename ultimo, que es el que dejará este archivo con el nombre del original, no se renombra con lo que me quedo sin el archivo. No se que hago mal para que no aparezca el archivo aux.dat en el directorio del proyecto. Ya probé a poner la ruta completa pero tampoco funciona.
¿Pueden ayudarme?

ecfisa
09-12-2016, 10:30:29
Hola.

Desconozco el por que del cálculo de los bloques para borrar un registro de manera secuencial. Pero, básicamente un borrado de ese tipo consiste en crear un archivo temporal, para luego recorrer el archivo original e ir copiando al temporal los elementos diferentes al que debe ser borrado.
Por último se borra el archivo original y se renombra al archivo temporal como el original.

Básicamente sería algo similar a este ejemplo:

#include <fstream>
...
struct DatosPersonales {
...
DatosPersonales();
void deleteID( const int id );
...
private:
struct {
int id;
char nombre[20];
char apellidos[30];
int edad;
} Datos;

char* FILE_NAME;
char* FILE_TEMP;
...
};

DatosPersonales::DatosPersonales()
{
FILE_NAME = "original.dat";
FILE_TEMP = "temporal.dat";
...
}

void DatosPersonales::deleteID( const int id )
{
std::fstream fsIn( FILE_NAME, std::fstream::in );
std::fstream fsOut( FILE_TEMP, std::fstream::out );

while ( ! fsIn.eof() ) {
fsIn.read( reinterpret_cast<char*>( &Datos), sizeof( Datos ) );
if ( !fsIn.eof() && Datos.id != id )
fsOut.write( reinterpret_cast<char*>( &Datos), sizeof( Datos ) );
}

fsIn.close();
fsOut.close();

std::remove( FILE_NAME );
std::rename( FILE_TEMP, FILE_NAME );
}
...

Uso:

...
{
DatosPersonales dp;
...
dp.deleteID( 1 );
...

Claro está que borrar de modo secuencial una estructura solo se justifica en calidad de prueba, ya que es significativamente mas eficiente hacerlo de forma aleatoria.

Saludos :)

aguml
09-12-2016, 15:07:46
Lo de los bloques es para no tener que ir copiando estructura por estructura, o sea obtengo la posición del dato que quiero buscar y a partir de esa dirección obtengo el número de bytes que hay desde el inicio hasta esa posición y lo mismo para lo que hay después. Con eso hago solo dos write y me ahorro tener que recorrer todo el archivo leyendo y escribiendo dato a dato.
Lo que dices de "hacerlo de forma aleatoria" ¿que es eso?

ecfisa
09-12-2016, 19:42:42
Hola.

En este enlace tenes el tema bastante bién explicado: Archivos de acceso aleatorio (http://c.conclase.net/ficheros/?cap=004)

Saludos :)

aguml
09-12-2016, 22:50:28
Ya entendí aunque tiene algún que otro inconveniente ya que si quiero eliminar un id determinado y no están ordenados ya no sirve este método y si lo están pero no son todos correlativos también habría problemas. Para usarlo tendría que mostrar todos los registros y poner por ejemplo el contador de registros leídos delante de cada registro usando dicho contador para elegir el registro a borrar.
Sigo sin ver el porque no se crea el archivo auxiliar.

ecfisa
09-12-2016, 23:03:48
...
Sigo sin ver el porque no se crea el archivo auxiliar.
Lamentablemente no puedo probar tu código por que carezco de algunas funciones y datos para hacerlo, pero si revisas el código del mensaje #2 (http://www.clubdelphi.com/foros/showpost.php?p=511632&postcount=2), verás que hace sin problemas esa taréa.
Es decir:

Crea el archivo auxiliar
Pasa los registros que no se eliminarán
Elimina el archivo original
Renombra el archivo auxiliar como el original

Si te pudiera resultar útil avisame y te adjunto los fuentes de la prueba.

Saludos :)

aguml
10-12-2016, 13:50:58
Misterios de Windows. Cambié el nombre del archivo auxiliar de "aux.dat" a "temp.dat" y ya funciona. Sólo falla con el nombre de archivo "aux.dat". Eso me da que pensar que ha fallado algo en windows y al crear el archivo no permite hacerlo porque intente sobre escribirlo y al no existir pues... no se, son especulaciones. Luego cuando llegue intentare mover todo a otra carpeta y ver que pasa.