1

Hol, tengo el siguiente código que funciona. El código escribe o lee en un archivo .dat :

int main()
{

    setlocale(LC_ALL, "");//para los acentos

    vector<Cliente> clientes;
    vector<Chofer> choferes;

    bool leer=true;
    if(!leer)
    {
        ofstream  archivo("cliente.dat",ios::binary);
        cout <<  "escribir";
        if(!archivo)
        {

            cout << "error";
        }
        else
        {
            Cliente cli=Cliente();
            cli.SetIdentificacion("11");
            cli.SetApellidos("jv");
            cli.SetDireccion("hbhk");
            cli.SetNombre("hkbhjkb");
            cli.SetTelefono("bj");
            cli.SetEmail("hbjhbj");

            clientes.push_back(cli);

            for(Cliente c:clientes)
            {
                archivo.write(reinterpret_cast<const char*>(&c),
                              sizeof(Cliente));
            }
        }
    }
    else
    {

        cout <<"leer";

        ifstream lectura("cliente.dat",ios::in);

        while(lectura&&!lectura.eof())
        {

            Cliente cl=Cliente();

            lectura.read(reinterpret_cast<char*>(&cl),
                         sizeof(Cliente));

            cout << cl.GetIdentificacion() << endl;
            cout << cl.GetApellidos() << endl;
            cout << cl.GetEmail() << endl;

        }


    }

    return 0;
}

introducir la descripción de la imagen aquí introducir la descripción de la imagen aquí

Hasta aquí todo bien , pero cuando quiero llamarlo desde un método me devuelve caracteres extraños:

void lecturaCliente()
{
    ifstream lectura("cliente.dat",ios::in);

    while(lectura&&!lectura.eof())
    {

        Cliente cl=Cliente();

        lectura.read(reinterpret_cast<char*>(&cl),
                     sizeof(Cliente));

        cout << cl.GetIdentificacion() << endl;
        cout << cl.GetApellidos() << endl;
        cout << cl.GetEmail() << endl;

    }


}
bool escrituraCliente(vector<Cliente> clientes)
{
    ofstream  archivo("cliente.dat",ios::binary);

    if(clientes.size()>0)
    {
        for(Cliente c:clientes)
        {
            cout << c.GetIdentificacion()<<"escr"<<endl;
            archivo.write(reinterpret_cast<const char*>(&c),
                          sizeof(Cliente));
        }
        return true;
    }
    else
    {
        return false;
    }


}
int main()
{

    setlocale(LC_ALL, "");//para los acentos

    vector<Cliente> clientes;
    vector<Chofer> choferes;

    bool leer=true;
    if(!leer)
    {
        ofstream  archivo("cliente.dat",ios::binary);
        cout <<  "escribir";
        if(!archivo)
        {

            cout << "error";
        }
        else
        {
            Cliente cli=Cliente();
            cli.SetIdentificacion("11");
            cli.SetApellidos("jv");
            cli.SetDireccion("hbhk");
            cli.SetNombre("hkbhjkb");
            cli.SetTelefono("bj");
            cli.SetEmail("hbjhbj");

            clientes.push_back(cli);

            for(Cliente c:clientes)
            {
                archivo.write(reinterpret_cast<const char*>(&c),
                              sizeof(Cliente));
            }
        }
    }
    else
    {

        cout <<"leer";

        lecturaCliente();

    }

 
    return 0;
}

Como se observa, el mismo código que tenía en el main simplemente lo pase a un método, eso fue todo lo que hice. Porque cuando invoco el mismo código desde un método devuelve caracteres extraños, como caracteres ascii o algo así es lo que devuelve!!

introducir la descripción de la imagen aquí Esta es la clase cliente :

#ifndef CLIENTE_H
#define CLIENTE_H
#include <string>
using std::string;
//Se utiliza std::string  para poder hacer uso  de  el  tipo string  en la clase

class Cliente
{
    public:
        Cliente();

        string GetIdentificacion() { return Identificacion; }
        void SetIdentificacion(string val) { Identificacion = val; }
        string GetNombre() { return Nombre; }
        void SetNombre(string val) { Nombre = val; }
        string GetApellidos() { return Apellidos; }
        void SetApellidos(string val) { Apellidos = val; }
        string GetDireccion() { return Direccion; }
        void SetDireccion(string val) { Direccion = val; }
        string GetTelefono() { return Telefono; }
        void SetTelefono(string val) { Telefono = val; }
        string GetEmail() { return Email; }
        void SetEmail(string val) { Email = val; }

    protected:

    private:
        string Identificacion;
        string Nombre;
        string Apellidos;
        string Direccion;
        string Telefono;
        string Email;
};

#endif // CLIENTE_H
Richard Víquez Pérez
  • 1,343
  • 1
  • 5
  • 23

1 Answers1

2

Problema.

El problema no es la lectura de archivos ni el hacer la lectura en un lugar u otro. El problema es que no sabes cómo funciona la clase std::string, lo voy a explicar brevemente.

Un std::string es un objeto que ayuda a manejar una cadena de caracteres, esta cadena puede cambiar de tamaño (crecer o menguar) por lo que internamente maneja memoria dinámica. Vamos a suponer que para ello guarda dos variables: un puntero y una longitud.

 /--------------+----------------\
 | longitud: 0  | búfer: nullptr |
 \--------------+----------------/

En un estado inicial, el std::string tendrá el puntero a nulo y una longitud cero, pero si le asignamos una cadena, reservará memoria para un búfer y actualizará su puntero y longitud:

/--------------+----------------\
| longitud: 10 | búfer: 0x00001 |
\--------------+-----------|----/
                           |
                           \    | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|
                            +-->|--+--+--+--+--+--+--+--+--+--+--+
                                | H| o| l| a|  | M| u| n| d| o| !|

Fíjate que el objeto std::string está alojado en un sitio diferente al del búfer en que la cadena está alojada, por eso cuando copias binariamente dicho objeto no estarás copiando binariamente la cadena, estarás copiando un número y un puntero; ese puntero sólo tendrá sentido en el contexto, ámbito y ejecución en que se creó el objeto std::string que lo maneja, fuera de esas condiciones el puntero apuntará a memoria que no se reservó ni rellenó con datos coherentes, por ello obtienes caracteres extraños.

¡Un momento! Entonces ¿Por qué funciona a veces? No iré al extremo de afirmar que no es posible ni aunque lo firme el propio Bjarne Stroustrup1 con su propia sangre en un trozo de su propia piel, ya que existe una cadena de casualidades que pueden da lugar a que pudiera poder ser posible que pudiera llegar a pasar lo que observas; lo puedes intuir leyendo esta respuesta.

Solución.

Para guardar y leer cadenas de texto en y desde archivos, deberás guardar por separado su longitud y contenido, te aconsejo hacerlo en funciones por separado:

void serializa(std::ostream &o, const std::string &s)
{
    std::string::size_type longitud = s.length();
    o.write(reinterpret_cast<const char*>(&longitud), sizeof(longitud));
    for (const auto &c : s)
        o.put(c);
}

void deserializa(std::istream &i, std::string &s)
{
    std::string::size_type longitud{};
    s.clear();

    o.read(reinterpret_cast<const char*>(&longitud), sizeof(longitud));
    for (char c; longitud; --longitud)
    {
        i.get(&c);
        s.push_back(c);
    }
}

Con estas funciones y siguiendo las prácticas de C++ moderno tu código podría parecerse a:

if (leer)
{
    // ifstream es de lectura ('i'nput 'f'ile 'stream'), no hace falta el ios::in
    if (ifstream lectura{"cliente.dat"}) 
        while (lectura)
        {
            string Identificacion;
            string Nombre;
            string Apellidos;
            string Direccion;
            string Telefono;
            string Email;
            deserializa(lectura, Identificacion);
            deserializa(lectura, Nombre);
            deserializa(lectura, Apellidos);
            deserializa(lectura, Direccion);
            deserializa(lectura, Telefono);
            deserializa(lectura, Email);

            // Constructor por defecto, no hace falta = Cliente();
            Cliente cl;
            cl.SetIdentificacion(Identificacion);
            cl.SetNombre(Nombre);
            cl.SetApellidos(Apellidos);
            cl.SetDireccion(Direccion);
            cl.SetTelefono(Telefono);
            cl.SetEmail(Email);
        }
    else
        cout << "No se pudo abrir el archivo";
}
else
{
    if (ofstream escritura{"cliente.dat"}) 
    {
        Cliente cli=Cliente();
        cli.SetIdentificacion("11");
        cli.SetApellidos("jv");
        cli.SetDireccion("hbhk");
        cli.SetNombre("hkbhjkb");
        cli.SetTelefono("bj");
        cli.SetEmail("hbjhbj");

        serializa(escritura, cli.GetIdentificacion());
        serializa(escritura, cli.GetApellidos());
        serializa(escritura, cli.GetDireccion());
        serializa(escritura, cli.GetNombre());
        serializa(escritura, cli.GetTelefono());
        serializa(escritura, cli.GetEmail());
    }
    else
        cout << "No se pudo escribir el archivo";
}

1

.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82