I have a quite difficult problem with binary files. I have been asked to make a program that stores information in files, but in Sequential mode. As I am not allowed to modify things in sequential mode directly, I made a function that first reads the file until I found the correct registry, while copying the other registries into an auxiliary file. when I finished modifying what I needed, I copy it to the auxiliary file and resume copying. When finished, I copy everything from the auxiliary file to the original one. I have made this following various examples of binary files, yet my program does some weird things. It begins eating all the information I wrote, leaving only the last entry (something like infinitely truncating), And even worse than that, there is a part (labeled the “pesky part”) that starts an infinite loop that has no sense (it’s as if the file had infinite size) I have chacked the logic like a thousand times and I don’t seem to find any mistake, I don’t know if you can help me, if I am missing something important.
Here are the classes I’m using
class Cliente{
public:
int numCuenta;
char dni[10];
char nombre[40];
};
class Cuenta{
private:
int numCuenta;
double monto;
int numDuenhos;
public:
const static double MONTO_MIN = 100.0;
Cuenta(){
numCuenta = 0;
monto = 0;
numDuenhos = 0;
}
int getnumCuenta(){
return numCuenta;
}
void setnumCuenta(int numCuenta){
this->numCuenta= numCuenta;
}
int getnumDuenhos(){
return numDuenhos;
}
void setnumDuenhos(int numDuenhos){
this->numDuenhos= numDuenhos;
}
double getMonto(){
return monto;
}
void setMonto(double monto){
this->monto = monto;
}
};
and the problematic function
void modificarCuenta() {
Cuenta aux;
Cliente c;
ifstream rep_cuentas("cuentas.bin");
ofstream buf_cuentas("cuentas_rep.bin",ios::out | ios::trunc | ios::binary);
if(!rep_cuentas) {
cout <<endl << "Error al leer fila principal";
}
else if(!buf_cuentas) {
cout << endl << "Error al abrir el archivo buffer";
}
else {
cout <<endl << "Ingrese el numero de cuenta a modificar: "; //id of entry
int num_cuenta;
cin >> num_cuenta;
ifstream rep_clientes("clientes.bin");
ofstream buf_clientes("cilentes_rep.bin",ios::out | ios::trunc | ios::binary);
//este archivo es necesario, por eso termina si no lo lee
if (!rep_clientes) {
cerr << "Error al Abrir el Archivo de Clientes" << endl;
return;
}
rep_cuentas.read(reinterpret_cast<char *>(&aux),sizeof(Cuenta));
while(!rep_cuentas.eof()){
if(aux.getnumCuenta() == num_cuenta){
rep_clientes.read(reinterpret_cast<char *>(&c),sizeof(Cliente));
while(!rep_clientes.eof()){
if(c.numCuenta == num_cuenta){
cout << "DNI del Cliente: " << c.dni << endl; //old dni
cout << "Nombre del Cliente: " << c.nombre << endl; // old name
cout << "Modificar estos datos? (1 para confirmar): ";
int opc;
cin >> opc;
if (opc == 1){
c.numCuenta = aux.getnumCuenta();
cout << endl << "Ingrese nuevo DNI: "; //new dni
cin >> c.dni;
cout << endl << "Ingrese nuevo Nombre: "; //new name
cin >> c.nombre;
}
}
buf_clientes.write(reinterpret_cast<char *>(&c),sizeof(Cliente));
rep_clientes.read(reinterpret_cast<char *>(&c),sizeof(Cliente));
}
int num = aux.getnumDuenhos();
while(true){
cout << endl << "Desea ingresar mas duenhos? (1 para confirmar): "; //appending new user?
int op;
cin >> op;
if (op == 1){
c.numCuenta = aux.getnumCuenta();
cout << endl << "Ingrese nuevo DNI: "; //new dni
cin >> c.dni;
cout << endl << "Ingrese nuevo Nombre: "; //new name
cin >> c.nombre;
num++;
buf_clientes.write(reinterpret_cast<char *>(&c),sizeof(Cliente));
}
else{
aux.setnumDuenhos(num);
break;
}
}
}
buf_cuentas.write(reinterpret_cast<char *>(&aux),sizeof(Cuenta));
rep_cuentas.read(reinterpret_cast<char *>(&aux),sizeof(Cuenta));
}
rep_clientes.close();
buf_clientes.close();
}
rep_cuentas.close();
buf_cuentas.close();
ofstream rcuentas("cuentas.bin",ios::out | ios::trunc| ios::binary);
ifstream bcuentas("cuentas_rep.bin");
if(!rcuentas) {
cout << endl << "Error al abrir la fila principal";
}
else if(!bcuentas) {
cout << endl << "Error al abrir el archivo buffer";
}
else{
bcuentas.read(reinterpret_cast<char *>(&aux),sizeof(Cuenta));
while(!bcuentas.eof()){
rcuentas.write(reinterpret_cast<char *>(&aux),sizeof(Cuenta));
bcuentas.read(reinterpret_cast<char *>(&aux),sizeof(Cuenta));
}
rcuentas.close();
bcuentas.close();
ofstream rclientes("clientes.bin",ios::out | ios::trunc | ios::binary);
ifstream bclientes("clientes_rep.bin");
bclientes.read(reinterpret_cast<char *>(&c),sizeof(Cliente));
//pesky part
while(!bclientes.eof()){
rclientes.write(reinterpret_cast<char *>(&c),sizeof(Cliente));
bclientes.read(reinterpret_cast<char *>(&c),sizeof(Cliente));
}
//end of pesky part
bclientes.close();
rclientes.close();
cout << endl << "Modificacion Realizada con Exito" << endl; //confirmation text
}
}
In case you need it, this is the function to write a new entry, it works perfectly fine:
void crearCuenta(){
Cuenta aux;
aux.setnumCuenta(0);
ifstream rcuentas("cuentas.bin");
if(!rcuentas){
cout<< endl <<"Primer uso del Sistema detectado, generando numero de cuenta inicial" <<endl;
}
else {
rcuentas.read(reinterpret_cast<char *>(&aux),sizeof(Cuenta));
while(!rcuentas.eof()){
rcuentas.read(reinterpret_cast<char *>(&aux),sizeof(Cuenta));
}
rcuentas.close();
}
Cuenta cu;
cu.setnumCuenta(aux.getnumCuenta() + 1);
int num_duenhos = 0;
ofstream a_clientes("clientes.bin",ios::app |ios::binary);
while(true){
char dni[10];
cout << "Ingrese el DNI del Duenho: "; //new dni
Cliente c;
cin >> c.dni;
cout << "Ingrese el Nombre del Duenho: "; //new name
cin >> c.nombre;
c.numCuenta = cu.getnumCuenta();
num_duenhos++;
a_clientes.write(reinterpret_cast<char *>(&c),sizeof(Cliente));
cout << "Desea ingresar otro duenho? (escriba 1 para confirmar): "; //another entry?
int val;
cin >> val;
if (val != 1)
break;
}
cu.setnumDuenhos(num_duenhos);
while(true){
double monto;
cout << endl;
cout << "Ingrese el monto con el cual iniciara la cuenta:"; //numerical value (greater than 100)
cin >> monto;
if (monto < Cuenta:: MONTO_MIN){
cout << "Debe ingresar un valor mayor a " << Cuenta::MONTO_MIN << endl;
}
else{
cu.setMonto(monto - monto * 0.005);
break;
}
}
ofstream acuentas("cuentas.bin",ios::app| ios::binary);
if(!acuentas){
cout<< endl <<"ERROR en la fila secuencial" <<endl;
}
else{
acuentas.write(reinterpret_cast<char *>(&cu),sizeof(Cuenta));
acuentas.close();
}
cout << "Cuenta Guardada Satisfactoriamente con número: " << cu.getnumCuenta() << endl; //confirmation text
}
Don’t mind the spanish text, it’s just for user communication. Any help will be highly appreciated
For starters, you’re not opening the input in binary mode, so unless
you’re under Unix, you won’t see the byte image of the file on disk.
If I understand correctly, you expect to read the files you’ve
written with the same program; if so, reading and writing in different
modes will not work.
Secondly, you’re reading and writing complex data structures (
Cuenta,Cliente) usingistream::readandostream::write. This doesn’twork, except in a few rare cases. Whether binary or text, all files
have a format, and this format must be respected in the code. Your
classes are either PODs, or close enough to one, that you probably won’t
see the problem until you make some changes in your development
environment, but it’s still there. (The fact that you need a
reinterpret_castto do so should be a red flag here.) And it definitely will not work(except under Unix) if you read or write in text mode. Reading a file
written this way in text mode will return additional characters, or stop
before the end of the file, under Windows.
Also,
while ( file.eof() )is not a correct way to read a file. Theresults of
file.eof()are not reliable until after an input hasfailed. For text files, you would normally use:
or
Reading as you do,
should work, provided you fix the rest of the problems.
This is probably the reason for your infinite loop; the
istreamis inan error state for some reason other than end of file, so
eof()willnever become true.