Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Conflicto de Entrada/Salida en C++

Iniciado por Neodivert, 28 de Junio de 2009, 05:42:34 PM

« anterior - próximo »

Neodivert

Buenas, estoy creando una clase en C++ para crear y controlar archivos de idiomas para mis próximos programas. Por ejemplo, tengo un archivo "Prueba.txt" con el siguiente contenido:


<PREGUNTA_NOMBRE>
Hola! Como te llamas? :

<PREGUNTA_EDAD>
Muy bien \s, cuantos annos tienes? :

<RESPUESTA_EDAD>
Oh! Asi que tienes \i annos... \n


Al usar el método "Generar_Archivo" de mi clase se genera el siguiente fichero:


// Entero que indica el número de cabeceras guardadas en el fichero
// Entero que indica el tamaño del mayor texto guardado en el archivo.
PREGUNTA_EDAD                  1 Muy bien \s, cuantos annos tienes? :
PREGUNTA_NOMBRE           0 Hola! Como te llamas? :
RESPUESTA_EDAD               1 Oh! Asi que tienes \i annos... \n


Mi clase podrá acceder al fichero generado y mostrar el texto asociado a la cabecera que yo le pase como parámetro al método "Mostrar".  El 0 o 1 que separa cabecera y texto indica si en el texto se muestran variables. Por ejemplo, puedo llamar a mostrar "PREGUNTA_EDAD" con un string (char *) y que en vez de \s me muestre ese string.

Bueno, ahora que he explicado lo que pretendo, comento mi problema.

Sobre el mismo fichero anterior he montado un programa de prueba:


int main(){
   try{
       cLD A;
       char Nombre[30];
       int Edad;

       A.Generar_Archivo( "Prueba.txt" );


       A.Mostrar( "PREGUNTA_NOMBRE" );
       cin.getline( Nombre, 30 );

       A.Mostrar( "PREGUNTA_EDAD", Nombre );
       cin >> Edad;


       A.Mostrar( "RESPUESTA_EDAD", Edad );


   }catch( bad_alloc& ){
       cout << "Error de memoria" << endl;
   }catch( eEscritura& ){
       cout << "Error de escritura" << endl;
   }catch( eLectura& ){
       cout << "Error de lectura" << endl;
   }catch( eArchivo_No_Abierto& ){
       cout << "Error: No se pudo abrir el archivo solicitado" << endl;
   }

   cin.get();
}


El problema surge al pedir los datos. Por ejemplo después de

A.Mostrar( "PREGUNTA_NOMBRE" );

Se muestra en pantalla "Hola! Como te llamas? : "

Pero a continuación, al hacer

cin.getline( Nombre, 30 );

al tratar de escribir el nombre me va sobrescribiendo el mensaje anterior. Por ejemplo, si escribo que mi nombre es Moises, en la pantalla aparece:

MoisesComo te llamas?:

o si escribo la edad después de mostrar el mensaje "Muy bien \s, cuantos annos tienes? :" por pantalla me aparece:

19y bien Moises, cuantos annos tienes? :

El código del prototipo de la clase y de la declaración de los métodos "Generar_Archivo" y "Mostrar" es el siguiente:



#include<cstdarg>
#include<fstream>
#include"cLista_Ordenada.cpp"

/*
 Nombre: "Lingva Dosiero" (LD) v1.0
 Autor: Moises Bonilla (Neodivert)
 Fecha de inicio: 06/06/09
 Fecha de finalización:
 Descripcion:
*/


/*                             Constantes y variables globales                                 */
/***********************************************************************************************/

const int TAM_LINEA = 101;
const int TAM_CABECERA = 41;
int P_No_Liberados = 0;
class eEscritura : public exception {};
class eLectura : public exception {};
class eArchivo_No_Abierto : public exception {};


/*                                Prototipos de clases y funciones                             */
/***********************************************************************************************/



/*                        1. Estructura sCabecera y operadores asociados                       */

struct sCabecera {
   char Cad[TAM_CABECERA];
   unsigned int Pos;
   unsigned int Num_Lineas;
   bool Var;

   // Operadores necesarios para crear una lista ordenada sin repeticiones.
   bool operator < ( sCabecera &B ){
       return ( strcmp( Cad, B.Cad ) < 0 );
   }
   bool operator != ( sCabecera &B ){
       return ( strcmp ( Cad, B.Cad ) != 0 );
   }
};

ostream& operator << ( ostream &Salida, sCabecera X ){
   Salida << X.Cad << "    Pos( " << X.Pos << ")" << endl;
   return Salida;
}



/*                             2 Clase cLD y operadores asociados                              */

class cLD {
   private:
       cLista_Ordenada<sCabecera> *Lista;
       ifstream *Archivo;
       char *NA;
   public:
       // 2.1. Constructores y destructores
       cLD() throw( bad_alloc );
       cLD( char Nombre_Archivo[] ) throw( bad_alloc );
       ~cLD();

       // 2.2. Inicialización.
       bool Asociar( char Nombre_Archivo [] ) throw( bad_alloc );

       // 2.3. Funciones principales
       bool Generar_Archivo( char Nombre_Archivo[] ) throw( bad_alloc, eEscritura );
       void Mostrar( char Cabecera[], ... )
                           throw( bad_alloc, eEscritura, eLectura, eArchivo_No_Abierto );
};



/*                             3. Funciones externas utilizadas                                */

void Cambiar_Extension( char Nombre_Archivo[], char *Extension );
char *Extension( char Nombre_Archivo[] );



/*                              Definiciones de clases y funciones                             */
/***********************************************************************************************/

[...]

/*                                   2.3. Funciones principales                                */

bool cLD::Generar_Archivo( char Nombre_Archivo[] ) throw( bad_alloc, eEscritura ) {
   /*
       Esta función se encarga de tomar un archivo de origen y transformarlo en un archivo .ld
       listo para ser usado. El proceso se divide en 2 partes:

           1. Crear una lista con todas las cabeceras del archivo de origen, ordenadas de
              menor a mayor.
           2. Recorrer la lista de cabeceras y escribir cada cabecera con su texto asociado
              en el archivo .ld
   */

   unsigned int i;
   ifstream A_Origen( Nombre_Archivo, ios::binary );
   ofstream A_Destino;
   char Linea_Actual[TAM_LINEA];
   sCabecera Cabecera_Actual;
   char *Texto;
   int Num_Cabeceras;

   /*
       Max_Tam y Tam_Actual se usan para ir calculando el tamaño del mayor texto encontrado.
       Esta información se usará cuando se invoque a la función "Mostrar".
   */
   int Max_Tam = 0;
   int Tam_Actual = 0;


   Cabecera_Actual.Cad[0] = 0;

   delete NA;
   try{
       NA = new char [ strlen( Nombre_Archivo )];
       strcpy( NA, Nombre_Archivo );
       Cambiar_Extension( NA, "ld" );
   }catch( bad_alloc& ){
       throw;
   }


   A_Destino.open( NA, ios::binary );

   if( A_Origen.is_open() ){
       if( A_Destino.is_open() ){

           /*
               Parte 1 : Creación de la lista de cabeceras

               Por la forma en que está estructurado el siguiente código, siempre se inserta
               una primera cabecera (sin uso) en la lista. Dicha cabecera se desecha una vez
               la lista se ha completado.
           */

           try{
               Lista = new cLista_Ordenada<sCabecera>;
           }catch( bad_alloc& ){
               throw;
           }

           while( !A_Origen.eof() ){
               A_Origen.getline( Linea_Actual, TAM_LINEA );
               // Comprobar si se trata de una cabecera:
               if( Linea_Actual[0] == '<' ){
                   // Inserta la cabecera anterior.
                   try{
                       Lista->Insertar( Cabecera_Actual );
                   }catch( bad_alloc& ){
                       delete Lista;
                       throw;
                   }

                   // Obtiene la cabecera
                   for( i=1; ( i<TAM_CABECERA-1 && Linea_Actual[i] != '>' && Linea_Actual[i] != '\000' ); ++i ){
                       if( Linea_Actual[i] >= 'a' && Linea_Actual[i] <= 'z' ){
                           Cabecera_Actual.Cad[i-1] = Linea_Actual[i]-32;
                       }else{
                           Cabecera_Actual.Cad[i-1] = Linea_Actual[i];
                       }
                   }
                   Tam_Actual = 0;
                   Cabecera_Actual.Cad[i-1] = '\000';
                   Cabecera_Actual.Pos = A_Origen.tellg();
                   Cabecera_Actual.Num_Lineas = 0;
                   Cabecera_Actual.Var = false;
               }else{
                   Tam_Actual += strlen( Linea_Actual );
                   Cabecera_Actual.Num_Lineas++;

                   if( strchr( Linea_Actual, '\\' ) ){
                       Cabecera_Actual.Var = true;
                   }
               }

               if( Cabecera_Actual.Cad[0] && Tam_Actual > Max_Tam ){
                   Max_Tam = Tam_Actual;
               }

           }


           // Inserta la última cabecera encontrada en el archivo.
           if( Lista->Tamanno() ){
               /*
                   Si el archivo tiene cabeceras, la última de ellas no se inserta en el código
                   anterior, por lo que se inserta aquí.
               */
               Lista->Extraer(); // Desecha la primera cabecera.
               if( Cabecera_Actual.Cad[0] != 0 ){
                   Lista->Insertar( Cabecera_Actual );
               }
           }


           try{
               Texto = new char [Max_Tam+1];
           }catch( bad_alloc& ){
               throw;
           }

           Num_Cabeceras = Lista->Tamanno();


           /*
               Al comienzo del archivo se escriben dos enteros sin signo, uno para indicar el
               número de cabeceras presente en el archivo, y otra para indicar el tamaño del
               mayor texto guardado en dicho archivo.
           */
           A_Destino.write( reinterpret_cast<char *>( &Num_Cabeceras ), sizeof( unsigned int ) );
           if( !A_Destino.good() ){
               delete Lista;
               A_Origen.close();
               A_Destino.close();
               throw eEscritura();
           }
           A_Destino.write( reinterpret_cast<char *>( &Max_Tam ), sizeof( unsigned int ) );
           if( !A_Destino.good() ){
               delete Lista;
               A_Origen.close();
               A_Destino.close();
               throw eEscritura();
           }

           while( Lista->Tamanno() ){
               /*
                   Se va extrayendo las cabeceras una a una de la lista y se van escribiendo
                   en el archivo (Sólo el texto de la propia cabecera).
               */
               Cabecera_Actual = Lista->Extraer();
               A_Origen.clear();
               A_Origen.seekg( Cabecera_Actual.Pos );

               A_Destino.write( Cabecera_Actual.Cad, TAM_CABECERA );
               if( !A_Destino.good() ){
                   delete Lista;
                   A_Origen.close();
                   A_Destino.close();
                   throw eEscritura();
               }

               /*
                   Dependiendo de si en el texto actual se mostrarán o no las variables pasadas
                   como parámetros a la función Mostrar, se escribirá un 1 o un 0
                   respectivamente, después de la cabecera.
               */
               if( Cabecera_Actual.Var ){
                   A_Destino.write( " 1 ", 3 );
               }else{
                   A_Destino.write( " 0 ", 3 );
               }
               if( !A_Destino.good() ){
                   delete Lista;
                   A_Origen.close();
                   A_Destino.close();
                   throw eEscritura();
               }

               /*
                   A continuación se escribe el texto asociado a la cabecera actual y se
                   inserta un salto de línea.
               */
               Texto[0] = 0;
               for( i=0; i<Cabecera_Actual.Num_Lineas; ++i ){
                   A_Origen.getline( Linea_Actual, TAM_LINEA );
                   if( strlen(Linea_Actual) > 1 ){
                       strcat( Texto, Linea_Actual );
                   }
               }

               A_Destino.write( Texto, Max_Tam );
               A_Destino.put( '\n' );
               if( !A_Destino.good() ){
                   /*
                       En caso de un error de escritura se vacía la lista de cabeceras, se cierran
                       los archivos y se lanza la excepción correspondiente.
                   */
                   delete Lista;
                   A_Origen.close();
                   A_Destino.clear();
                   A_Destino.close();
                   throw eEscritura();
               }

           }

           A_Origen.close();
           A_Destino.close();

           delete Texto;
           return true;
       }
       A_Origen.close();
       return false;
   }
   return false;
}


void cLD::Mostrar( char Cabecera[], ... )
                               throw( bad_alloc, eEscritura, eLectura, eArchivo_No_Abierto ) {
   unsigned int Ini = 0;
   unsigned int Fin;
   int Comp = 1;
   int Mitad;
   char *Texto = NULL;
   char Cabecera_Archivo[TAM_CABECERA];
   int Max_Tam;
   int Pos;
   char Variables;
   va_list Lista;

   char *s_arg;
   int i_arg;
   double d_arg;


   Archivo->open( NA );
   if( Archivo->is_open() ){
       /*
           Lee los dos enteros sin signo, el que indica el número de cabeceras se guarda en la
           variable "Fin" y el que indica el tamaño del mayor texto guardado en el archivo se
           guarda en "Max_Tam".
       */
       Archivo->read( reinterpret_cast<char *>( &Fin ), sizeof( unsigned int ) );
       Archivo->read( reinterpret_cast<char *>( &Max_Tam ), sizeof( unsigned int ) );

       if( !Archivo->good() ){
           Archivo->clear();
           Archivo->close();
       }


       while( Fin > Ini ){

           /*
               Se realiza una búsqueda dicotómica de la cabecera solicitada. El resultado de la
               comparación entre dicha cabecera y la actual se guarda en la variable Comp.
           */
           Archivo->clear();
           Mitad = (Ini+Fin)/2;
           Pos = Mitad*(Max_Tam+TAM_CABECERA+4)+2*sizeof(unsigned int);
           Archivo->seekg( Pos );

           Archivo->getline( Cabecera_Archivo, TAM_CABECERA );

           Comp = strcmp( Cabecera, Cabecera_Archivo );
           if( Comp > 0 ){
               Ini = Mitad+1;
           }else if( Comp < 0 ){
               Fin = Mitad;
           }else{
               Ini = Fin;
           }

       }

       if( Comp == 0 ){
           // Cabecera encontrada.
           Texto = new char [Max_Tam];

           /*
               Se lee el caracter que indica si el texto asociado a la cabecera buscada
               debe mostrar las posibles variables pasadas como parámetros.
           */
           Archivo->clear();
           Archivo->seekg( Pos+TAM_CABECERA+1 );
           Archivo->get( Variables );

           /*
               Se lee el texto asociado.
           */
           Archivo->clear();
           Archivo->seekg( Pos+TAM_CABECERA+3 );
           Archivo->getline( Texto, Max_Tam );
           Archivo->clear();


           if( Variables == '0' ){
               /*
                   Muestra el texto sin variables.
               */
               cout.clear();
               cout << Texto;
           }else{
               /*
                   Va mostradno el texto caracter a caracter con variables. Para ello se usa
                   una lista para albergar la lista variable de posibles parámetros.
               */
               va_start( Lista, Cabecera );
               for( int i=0; Texto[i]!=0; ++i ){
                   if( Texto[i] != '\\' ){
                       cout << Texto[i];
                   }else{
                       if( ( i+1 < Max_Tam ) ){
                           switch ( Texto[i+1] ) {
                               case 'i':
                                   i_arg = va_arg( Lista, int );
                                   cout << i_arg;
                               break;
                               case 's':
                                   s_arg = va_arg( Lista, char* );
                                   cout << s_arg;
                               break;
                               case 'd' | 'f':
                                   d_arg = va_arg( Lista, double );
                                   cout << d_arg;
                               break;
                               case 'c':
                                   i_arg = va_arg( Lista, int );
                                   cout << (char)(i_arg);
                               break;
                               default:
                                   cout << endl;
                               break;
                           };
                           i++;
                       }
                   }
               }
               va_end( Lista );


           }
           delete Texto;
       }else{
           cout << "[" << Cabecera << "]" << endl;
       }

       Archivo->clear();
       Archivo->close();
   }else{
       throw eArchivo_No_Abierto();
   }


}
[...]



He probado a usar el método "clear" sobre cout y cin, también el método "ignore" por si acaso, pero sigue igual. ¿Alguna solución? ^^U

Por cierto, hace bastante tiempo cree una función sencillisima para limpiar el buffer de entrada, pero ya no estoy seguro de si es lo correcto (aunque al usarlo no me da problemas). ¿El siguiente código es correcto?


inline void Limpiar_Buffer(){
      cin.clear();
      cin.seekg( 0, ios::end );
      cin.clear();
}


Muchas gracias.  ;)


Mars Attacks

Igual deberías usar el flush después de las llamadas de entrada y salida, aunque nunca he tenido que pegarme con algo así y hablo un poco de lo primero que se me ocurre.

Neodivert


También lo he probado sin resultados. ^^U

Gracias de todos modos. ;)






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.