0

Verán, tengo un inconveniente al momento de crear un fichero de texto, a este fichero yo le mando a guardar diferentes tipos de datos, tipo int, char, long, pero al momento en el que se crea el fichero este solo me guarda las palabras, no los números, los numeros me los muestra de esta manera Èè @QÀt, pero las palabras si me las muestra, lo curioso esta en que mi programa tambien tiene la opcion de cargar un fichero, entonces al momento de mandar a cargar ese fichero, en pantalla si me muestran los valores como deberian de ser.

Pondré un ejemplo, en mis datos de entrada tengo:

Nombre del paciente: Eugenio

Cedula del paciente: Jinotega

Numero de celular: 89647667

Edad del paciente: 20

Estado del Paciente: Critico

Numero de sala: 5

Tipo de expediente: 6

Tipo de enfermedad: 3

Al momento de guardar en fichero se me guarda esto:

œc@ Eugenio                                           Jinotega                      3êW            Critico Èè     @QÀt

Pero al momento de cargar el fichero que contiene estos datos me muestra esto:

Expediente 1:

Paciente: Eugenio

Cedula: Jinotega

Numero Celular: 89647667

Edad: 20

Condicion: Critico

Sala N: 5

Tipo de Expediente: 6

Tipo de enfermedad: 3

Que son los datos que se me tendrían que almacenar en el fichero.

Adjunto el código completo, se trata de un programa que almacena "expedientes" de personas en una clínica, es un programa de aplicación de clases así que es algo básico.

#include <iostream>
#include <stdlib.h>
#include <fstream>
#include <string>

using namespace std;

//Clase donde se me mostrara los daros ingresados
class Almacenamiento
{
    public:
    virtual void mostrarINFO() {;};
};

//Clase Paciente
class Paciente
{
    private:
    char n_Paciente[50];     //Nombres y Apellidos
    char c_identidad[30];    //Cedula de indentidad
    long n_Movil;            //Numero celular
    int edad;               //Edad de las personas

    public:                 //Metodos
    Paciente(){};
    void datosPaciente();
    friend void operator>>(istream&ci, Paciente &p)
    {
        cin.ignore();
        cout<<"Nombre del paciente: "; cin.getline(p.n_Paciente,50);
        cout<<"Cedula del paciente: ";cin.getline(p.c_identidad,30);
        cout<<"Numero de celular: ";cin>>p.n_Movil;
        cout<<"Edad del paciente: ";cin>>p.edad;
    }
    friend void operator<<(ostream&ci, Paciente &p)
    {
        cout<<"Paciente: "<<p.n_Paciente<<endl;
        cout<<"Cedula: "<<p.c_identidad<<endl;
        cout<<"Numero Celular: "<<p.n_Movil<<endl;
        cout<<"Edad: "<<p.edad<<endl;
    }
};

//Clase Hospital
class Hospital
{
    private:        //Atributos
    int n_Sala;
    int t_Expediente;
    int t_Enfermedad;        
    public:
    Hospital(){};
    friend void operator>>(istream&ci, Hospital &h)
    {
        cin.ignore();
        cout<<"Numero de sala: ";cin>>h.n_Sala;
        cout<<"Tipo de expediente: ";cin>>h.t_Expediente;
        cout<<"Tipo de enfermedad: ";cin>>h.t_Enfermedad;
    }
    friend void operator<<(ostream&ci, Hospital &h)
    {
        cout<<"Sala N: "<<h.n_Sala<<endl;
        cout<<"Tipo de Expediente: "<<h.t_Expediente<<endl;
        cout<<"Tipo de enfermedad: "<<h.t_Enfermedad<<endl;
    }
};

//Clase Expediente, hija de la clase Almacenamiento
class Expedientes:public Almacenamiento
{
    Paciente p;
    Hospital h;
    char condicion[20];
    public:
    Expedientes(){};
    friend void operator>>(istream&ci, Expedientes *e)
    {
        cin>>e->p;
        cout<<"Estado del Paciente: ";
        cin>>e->condicion;
        cin>>e->h;
    }
    void mostrarINFO()
    {
        cout<<p;
        cout<<"Condicion: "<<condicion<<endl;
        cout<<h;
    }
};
int Menu();

int main(void)
{
    Almacenamiento *Lista[40];
    int num=0;

    while (1)
    {
        int opc=Menu();
        
        if (opc==5) //Libera
        {
            for (int i=0;i<num;i++)
            {
                delete Lista[i];
            }
            break;
        }
        else if(opc==1) //Agrega Expediente
        {
            Expedientes *exp1=new Expedientes();
            cin>>exp1;
            Lista[num++]=exp1;
        }
        else if(opc==2) //Mira el expediente
        {
            for (int i=0;i<num;i++)
            {
                cout<<"Expediente "<<i+1<<":\n";
                Lista[i]->mostrarINFO();
            }
        }
        else if (opc==3)        //Graba el Expediente en un fichero
        {
            char nFichero[30];
            cout<<"Ingrese el nombrel del Fichero: ";cin>>nFichero;
            ofstream fout;

            fout.open(nFichero,ios::out/*|ios::binary*/);
            if (!fout)
            {
                cout<<"Error..."<<endl;
            }
            for (int i=0;i<num;i++)
            {
                fout.write((char*)Lista[i],sizeof(Expedientes));
                cout<<i+1<<" Datos Guardados en: "<<nFichero<<endl;
            }
            fout.close();
        }
        else if (opc==4)    //Carga un expediente
        {
            char nFichero[40];
            cout<<"Nombre del fichero: ";cin>>nFichero;
            fstream fin(nFichero,ios::in/*|ios::binary*/);
            if (!fin)
            {
                cout<<"No se puede abrir el fichero: "<<endl;
            }
            else 
            {
                int cont=0;
                while (true)
                {
                    Expedientes *exp1=new Expedientes();
                    Lista[num]=exp1;
                    if (!fin.read((char*)Lista[num],sizeof(Expedientes)))
                    {
                        delete exp1;
                        break;
                    }
                    num++;
                    cont++;
                    
                }
                cout<<cont<<"Datos cargados de "<<nFichero<<endl;
                
            }
        }
    }
    

    system ("pause");
    return 0;
}

int Menu()
{
    int opc;
    cout<<"\t\t** Registro de Expedientes **"<<endl;
    cout << "\t\t1. Agregar Expediente"<<endl;  
    cout << "\t\t2. Ver Expediente"<<endl;  
    cout << "\t\t3. Grabar Expediente"<<endl;  
    cout << "\t\t4. Cargar Expediente"<<endl;  
    cout << "\t\t5. Salir"<<endl;  
    cout << "\t\t\tOpc: ";    
    cin >> opc; 
    return opc;
}
eferion
  • 49,291
  • 5
  • 30
  • 72
Asxoyr
  • 157
  • 9
  • Tu código tiente tantos errores y problemas que la respuesta será larga, densa e hiriente. El menor de tus problemas es lo que motiva tu pregunta. – PaperBirdMaster Apr 14 '21 at 07:11

2 Answers2

2

El menor de tus problemas es lo que motiva tu pregunta, tu código es erróneo a nivel de estilo, uso y funcionalidad. Por orden de aparición, los problemas de tu código son:

  1. Uso de cabeceras de : la cabecera <stdlib.h> es de C, pero estás programando en , no debes usar esa cabecera. Lee este hilo para saber más.
  2. Uso de formaciones de caracteres: En C++ moderno, el uso de formaciones de caracteres para almacenar datos de texto está en desuso, las formaciones de caracteres son poco flexibles y sus límites pueden sobrepasarse provocando fallos de memoria. Lo habitual en C++ moderno es usar std::string. Pero entonces tendrás otro problema.
  3. Si el constructor va a estar vacío, no lo definas: ¿Para qué quieres escribir de más? Ya has visto que no es necesario un constructor pues no lo defines en Almacenamiento aunque sí lo hagas en Paciente y Hospital.
  4. Las funciones friend son para otorgar acceso a datos privados del objeto: No tiene ningún sentido que una función friend esté dentro del objeto. Ya te lo había dicho en otra respuesta.
  5. Las sobrecargas de los operadores de inyección (<<) y extracción (>>) deben devolver el obtejo flujo (stream): Tú estás declarando esas funciones operador como void, lo cuál no sigue las definiciones del lenguaje para esos operadores. Ya te lo había dicho en otra respuesta.
  6. De nada sirve sobrecargar los operadores de inyección (<<) y extracción (>>) si después no usas el flujo: En tus operadores de inyección y extracción estás usando cin y cout en lugar del flujo (stream) que recibes como parámetro, haciendo que esos operadores sean inútiles. Ya te lo había dicho (muy por encima) en otra respuesta.
  7. De nada sirve sobrecargar los operadores de inyección (<<) y extracción (>>) si al final no los usas: No estás llamando al operador en tu código, simplemente llamas al read o write del flujo de archivo.
  8. No uses void en funciones que no reciben parámetros: En C++ las funciones que no reciben parámetros se declaran y definen con paréntesis vacíos, es en C donde es necesario poner void.
  9. El objeto ofstream es por definición de salida: output file stream es por definición un objeto de salida de flujo a archivo, no necesitas abrirlo con ios::out.
  10. El objeto ifstream es por definición de entrada: input file stream es por definición un objeto de entrada de flujo a archivo, no necesitas abrirlo con ios::in.
  11. Sobran comentarios: Poner comentarios para destacar lo evidente hace que el código sea más difícil de leer, no necesitas comentar que class Hospital es la //Clase hospital ¡ya lo dice el código!.

Para entender tu "problema", vamos a centrarnos en el séptimo punto. Tu objeto Paciente tiene este aspecto en memoria:

n_Paciente (50 bytes) c_identidad (30 bytes) n_Movil (8 bytes*) edad (4 bytes)
Eugenio Jinotega ? ?

Y eso es (casi) literalmente lo que se ve en memoria:

                                                                                                    11111111111111111111
000000000011111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111
012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
œc@ Eugenio                                           Jinotega                      3êW            Critico Èè     @QÀt
\__/\________________________________________________/\____________________________/\______/\__/\__________________/
  \                                    \                          \                     \     \     \           
   \                                    \___ Nombre (50 bytes)     \                     \     \     \___ Condición (20 bytes)
    \___ 4 bytes (posiblemente padding o vtable)                    \                     \     \___ Edad (4 bytes)
                                                                     \                     \___ Telefono (8 bytes)
                                                                      \___ CIdentidad (30 bytes)

Digo casi, porque no esperabas los cuatro bytes iniciales de padding o tal vez la tabla virtual que vemos al inicio de cada bloque de datos.

Los símbolos extraños que ves son la representación en caracteres de los datos numéricos, si hubieses guardado el número 1215261793 habrías visto escrito Hola porque la representación en caracteres de ese número coincide (casualmente) con esa palabra.

Si corregimos todos los disparates de tu código, podría parecerse a esto:

#include <iostream>

class Paciente
{
    char n_Paciente[50];    // Nombres y Apellidos
    char c_identidad[30];   // Cedula de indentidad
    long n_Movil;       // Numero celular
    int edad;       // Edad de las personas

public:
    friend istream &operator>>(istream &, Paciente &);
    friend ostream &operator<<(ostream &, const Paciente &);
};

class Hospital
{
    int n_Sala;
    int t_Expediente;
    int t_Enfermedad;        
public:
    friend istream &operator>>(istream &, Hospital &);
    friend ostream &operator<<(ostream &, const Hospital &);
};

class Expedientes : public Almacenamiento
{
    Paciente p; 
    Hospital h;
    char condicion[20];
public:
    friend istream &operator>>(istream &, Expedientes &);
    friend ostream &operator<<(ostream &, const Expedientes &);
};

istream &operator>>(istream &i, Paciente &p) { /* codigo de lectura de Paciente */ return i; }
ostream &operator<<(ostream &o, const Paciente &) { /* codigo de escritura de Paciente */ return o; }
istream &operator>>(istream &, Hospital &) { /* codigo de escritura de Hospital */ return i; }
ostream &operator<<(ostream &, const Hospital &) { /* codigo de escritura de Hospital */ return o; }
istream &operator>>(istream &, Expedientes &) { /* codigo de escritura de Expedientes */ return i; }
ostream &operator<<(ostream &, const Expedientes &) { /* codigo de escritura de Expedientes */ return o; }

int main()
{
    // Mucho código

    else if (opc==3)        //Graba el Expediente en un fichero
    {
        string nFichero;
        cout << "Ingrese el nombrel del Fichero: ";
        cin >> nFichero;

        if (ofstream fout{nFichero})
        {
            for (int i = 0; i < num; ++i)
            {
                const Expedientes &e = *Lista[i];
                fout << e;
            }
        }
        else
            cout << "Error..." << endl;
    }
    else if (opc==4)    //Carga un expediente
    {
        string nFichero;
        cout << "Nombre del fichero: ";
        cin>>nFichero;

        if (fstream fin{nFichero})
        {
            int cont = 0;
            while (fin)
            {
                Lista[num] = new Expedientes();
                fin >> *Lista[num];
                ++num;
                ++cont;
            }
        }
        else
            cout << "No se puede abrir el fichero: " << endl;
    }

    // Mucho código

    return 0;
}

*Suponiendo un long de 64 bits.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82
1

C++ no es C. En C las estructuras se suelen guardar volcando su memoria al archivo directamente... pero eso en C++ no va a funcionar:

fout.write((char*)Lista[i],sizeof(Expedientes));

Por varias razones:

  • C++ soporta polimorfismo y funciones virtuales. El estándar no indica cómo se ha de soportar esto, pero los compiladores suelen incluir información adicional en los objetos. Es decir, los objetos que hacen uso de herencia y polimorfismo pueden tener información propia del compilador ... esa información no debería ir nunca al archivo

  • Un puntero no almacena valores sino posiciones de memoria, volcar esa posición de memoria al archivo (en vez de los valores almacenados en esa posición de memoria), hará que pierdas información y que, al cargar los datos, el puntero apunte a una dirección de memoria que no será válida

Tienes que volcar los datos uno a uno (y lo mismo para leer). El problema que te plantea entonces esta parte es qué formato le vas a dar a cada campo para poder leerlo correctamente cuando llegue el momento.

for (int i=0;i<num;i++)
{
    if (Expediente *expediente = dynamic_cast<Expediente*>(Lista[i]))
    {
       // Guardamos `expediente`
    }
}

Por ejemplo, el guardado de pacientes podría ser:

Paciente * paciente = /*...*/
fout.write(paciente->n_Paciente, sizeof(paciente->n_Paciente));
fout.write(paciente->c_identidad, sizeof(paciente->c_identidad));
fout << paciente->n_Movil << '\t' << paciente->edad;

Y su lectura:

Paciente * paciente = /* ... */
fin.read(paciente->n_Paciente, sizeof(paciente->n_Paciente));
fin.read(paciente->c_identidad, sizeof(paciente->c_identidad));
fin >> paciente->n_Movil >> paciente->edad;

Nota que el tabulador debería ser descartado automáticamente al leer los datos numéricos

eferion
  • 49,291
  • 5
  • 30
  • 72