Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Diferenciar entrada de datos por consola

Estas en el tema de Diferenciar entrada de datos por consola en el foro de C/C++ en Foros del Web. Hola amigos, mi duda es respecto a poder identificar si el usuario ha introducido strings o números, y de ser así meterlos en variables enteras. ...
  #1 (permalink)  
Antiguo 13/04/2015, 12:37
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 10 años, 4 meses
Puntos: 1
Diferenciar entrada de datos por consola

Hola amigos, mi duda es respecto a poder identificar si el usuario ha introducido strings o números, y de ser así meterlos en variables enteras. He leído que la librería sstream puede ayudar.

He hecho un pequeño programa pero este solo reconoce si el usuario a introducido un 's' o números. y me gustaría mejorarlo para que reconozca cualquier string.

Código C++:
Ver original
  1. #include <iostream>
  2. #include <sstream>
  3.  
  4. using namespace std;
  5.  
  6. int main(){
  7.     double a = 0, b = 0, c = 0; string sal; istringstream o;
  8.     while (true){
  9.         cout << "Digitar los valores de 'a' y 'b' (positivos):" << endl;
  10.         sal.clear();
  11.         o.clear();
  12.         getline(cin, sal, '\n');
  13.         o.str(sal);
  14.         if (sal.back() == 's'){
  15.             break;
  16.         }else{
  17.             o >> a >> b;
  18.             try{
  19.                 if (b == 0) throw "b es cero.";
  20.                 if (a < 0) throw "a es menos que cero.";
  21.                 if (b < 0) throw "b es menor que cero.";
  22.                 c = a / b;
  23.                 cout << "Resultado: " << c << endl << endl;
  24.             }
  25.             catch (const char *err){
  26.                 cout << "Error: " << err <<  endl << endl;
  27.             }
  28.         }
  29.     }
  30.     cout << endl << "Presione cualquier tecla para salir....";
  31.     cin.sync();
  32.     cin.get();
  33.     return 0;
  34. }

saludos
  #2 (permalink)  
Antiguo 13/04/2015, 18:50
Avatar de vangodp  
Fecha de Ingreso: octubre-2013
Mensajes: 934
Antigüedad: 11 años, 1 mes
Puntos: 38
Respuesta: Diferenciar entrada de datos por consola

Es el ejemplo de esta pagina modificado: http://www.cplusplus.com/reference/locale/isdigit/

Código C++:
Ver original
  1. // isdigit example (C++)
  2. #include <iostream>       // std::cout
  3. #include <string>         // std::string
  4. #include <locale>         // std::locale, std::isdigit
  5. #include <sstream>        // std::stringstream
  6.  
  7. int main () {
  8.     std::locale loc;
  9.     std::string str;
  10.     bool salir = false;
  11.     int n;
  12.    
  13.     do {
  14.         std::cout << "Entre con el numero" << std::endl;
  15.         std::cin >> str;
  16.         if (/**/ isdigit(str[0], loc) /**/) {
  17.             std::stringstream ( str ) >> n;
  18.             std::cout << "Es correcto el numero?->" << n << std::endl << "1-si \n0-no\n>>>";
  19.             int opc;
  20.             std::cin >> opc;
  21.             if ( opc == 1 ){
  22.                 salir = true;
  23.             }
  24.        
  25.         }else{
  26.             std::cout << "No es un numero" << std::endl;
  27.  
  28.         }
  29.        
  30.     } while ( !salir );
  31.    
  32.    
  33.     std::cout << "El numero es " << n << std::endl;
  34.  
  35.     return 0;
  36. }

Aun que puedes usar el ejemplo con ctype: http://www.cplusplus.com/reference/cctype/isdigit/
  #3 (permalink)  
Antiguo 14/04/2015, 00:01
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Diferenciar entrada de datos por consola

También, ya que estás en C++, puedes aprovechar los templates:

Código C++:
Ver original
  1. #include <iostream>
  2. #include <stdexcept>
  3. #include <string>
  4.  
  5.  
  6. template< typename T >
  7. bool Convert( const std::string&, T& )
  8. {
  9.   return false;
  10. }
  11.  
  12. template< >
  13. bool Convert( const std::string& cadena, int& valor )
  14. {
  15.   try
  16.   {
  17.     valor = std::stoi( cadena );
  18.     return true;
  19.   }
  20.  
  21.   catch( const std::invalid_argument& )
  22.   {
  23.     return false;
  24.   }
  25. }
  26.  
  27. template< >
  28. bool Convert( const std::string& cadena, float& valor )
  29. {
  30.   try
  31.   {
  32.     valor = std::stof( cadena );
  33.     return true;
  34.   }
  35.  
  36.   catch( const std::invalid_argument& )
  37.   {
  38.     return false;
  39.   }
  40. }
  41.  
  42. int main( )
  43. {
  44.   std::string cad;
  45.  
  46.   std::cout << "Introduce un número entero: " << std::endl;
  47.   std::cin >> cad;
  48.  
  49.   int entero;
  50.   if( Convert( cad, entero ) )
  51.     std::cout << "El número es: " << entero << std::endl;
  52.   else
  53.     std::cout << "No es un número válido" << std::endl;
  54.  
  55.   std::cout << "Introduce un número decimal: " << std::endl;
  56.   std::cin >> cad;
  57.  
  58.   float decimal;
  59.   if( Convert( cad, decimal ) )
  60.     std::cout << "El número es: " << decimal << std::endl;
  61.   else
  62.     std::cout << "No es un número válido" << std::endl;
  63. }
  #4 (permalink)  
Antiguo 14/04/2015, 19:10
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 10 años, 4 meses
Puntos: 1
Respuesta: Diferenciar entrada de datos por consola

Excelentes alternativas, muchas gracias las estoy estudiando bien para entenderlas.
  #5 (permalink)  
Antiguo 14/04/2015, 19:28
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 10 años, 4 meses
Puntos: 1
Respuesta: Diferenciar entrada de datos por consola

Eferion, podrías explicarme cómo funciona el template vacío? Es que no le veo mucho sentido pero funciona muy bien en el programa.

Código C++:
Ver original
  1. template< > bool Convert(const string& cadena, int& valor)

Gracias.
  #6 (permalink)  
Antiguo 14/04/2015, 20:36
Avatar de vangodp  
Fecha de Ingreso: octubre-2013
Mensajes: 934
Antigüedad: 11 años, 1 mes
Puntos: 38
Respuesta: Diferenciar entrada de datos por consola

Eso es así por que en esas funciones el si sabe el formato que recibir por parámetros. Pero en la que pone <typename T> uno de los datos es genérico, o sea que puede recibir un char un int o cualquier otro tipo.

En resumen todas las funciones se llaman iguales, por el mismo nombre, lo que indica una sobrecarga. Lo que las distingue son sus argumentos, si el enviá 2 datos que no encajan correctamente en 2 de las funciones, seguramente en la que tiene la T como parámetro, esa sí se lo "traga todos los tipos". Lo único que hace es que si es invocada la que tiene la T retornará false.

Tienes una plantilla que recibe un const string& y un int&, otra que recibe un const string& y un float&, y por ultimo una que recibe const string& y un T&(cualquier tipo). ¿Si envio Convert( "lol", 32) sabes cual invoca? Invoca la const string& y un int&. Por otro lado si envió Convert( "lol", 23.42) invocará la const string& y un float&. ¿Y si envio? Convert( "Lol", "Lol" ) ??? será la const string& y un T&, esa retorna false si eso pasa. =)

¿Se parecen a if else más elaborados a que si XD?. si(if) envio un string y un float entra en una, si(else if, o otro if) envio un string y un int entra en otra, si no(else) envio un string y otro tipo de dato que no sea int o float entonces(else) entra en la genérica (string y T).

No tengo mucha exp con templates pero he leido algo sobre ellas.
  #7 (permalink)  
Antiguo 15/04/2015, 00:14
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Diferenciar entrada de datos por consola

Cita:
Iniciado por dmorill Ver Mensaje
Eferion, podrías explicarme cómo funciona el template vacío? Es que no le veo mucho sentido pero funciona muy bien en el programa.

Código C++:
Ver original
  1. template< > bool Convert(const string& cadena, int& valor)

Gracias.
Ese template es una especialización. El compilador, al "ver" esta función, se pondrá a buscar y localizará el template "base", como ambos tienen la misma firma (mismo return y mismo nombre y mismo número de argumentos aunque de diferente tipo, que para eso es un template), entonces entiende que se trata de una especialización del template... en este caso no hace falta indicar el typename porque el compilador es capaz de deducirlo de forma implícita. Dicho de otra forma:

El compilador sabe que, en el template base, el tipo del segundo argumento de la función depende del template... si se encuentra una especialización en la que ese argumento en vez de ser "T" es un tipo concreto, entonces tiene la certeza de que ese tipo concreto es el que sustituye a "T".

El template base lo configuré para que retonrase "false", ya que el template genérico no sabe cómo realizar una conversión abstracta... pero sienta una base a partir de la cual tu puedes, mediante especializaciones del template, añadir todas las opciones que creas oportunas. Al final lo que te queda es un sistema completo de conversión en una sola función.
  #8 (permalink)  
Antiguo 15/04/2015, 04:06
Avatar de vangodp  
Fecha de Ingreso: octubre-2013
Mensajes: 934
Antigüedad: 11 años, 1 mes
Puntos: 38
Respuesta: Diferenciar entrada de datos por consola

jajaja ve! Era un 'if else' de templates jaja. Pfff Que complicadas son las plantillas cachis.

He visto pocas cosas con platillas(siempre intento evitarlas >_<), pero realmente el que las sabe sacar partido hacen maravillas con ellas. Otro día vi como se hacia un un nodo de una lista simples de una estructura con plantillas, no tenia ni idea de que se pudiera hacer esto. jajaja

A ver si un día llego a las plantillas, pero eso me parece ya muy hard para mi. >_<
  #9 (permalink)  
Antiguo 15/04/2015, 07:27
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Diferenciar entrada de datos por consola

Cita:
Iniciado por vangodp Ver Mensaje
jajaja ve! Era un 'if else' de templates jaja. Pfff Que complicadas son las plantillas cachis.

He visto pocas cosas con platillas(siempre intento evitarlas >_<), pero realmente el que las sabe sacar partido hacen maravillas con ellas. Otro día vi como se hacia un un nodo de una lista simples de una estructura con plantillas, no tenia ni idea de que se pudiera hacer esto. jajaja

A ver si un día llego a las plantillas, pero eso me parece ya muy hard para mi. >_<
La clave es pensar de forma diferente, conocer sus limitaciones (de los templates) y cómo sortear dichas limitaciones... y luego dejar todo bien documentado para no volverse loco.

vangodp, tu comentario me ha inspirado. Un ejemplo de una lista simple usando templates... la gracia está en que admite diferentes tipos de datos:

Código C++:
Ver original
  1. #include <iostream>
  2. #include <string>
  3.  
  4. class NodoBase
  5. {
  6.   public:
  7.  
  8.     NodoBase( )
  9.       : _siguiente( nullptr )
  10.     { }
  11.  
  12.     virtual ~NodoBase( )
  13.     { }
  14.  
  15.     void SetSiguiente( NodoBase* siguiente )
  16.     { _siguiente = siguiente; }
  17.  
  18.     NodoBase* Siguiente( ) const
  19.     { return _siguiente; }
  20.  
  21.     virtual void Pintar( std::ostream& out ) const = 0;
  22.  
  23.   private:
  24.  
  25.     NodoBase* _siguiente;
  26. };
  27.  
  28. template< typename _TYPE_ >
  29. class Nodo : public NodoBase
  30. {
  31.   public:
  32.     Nodo( _TYPE_ valor )
  33.       : _valor( valor )
  34.     { }
  35.  
  36.     ~Nodo( )
  37.     { }
  38.  
  39.     virtual void Pintar( std::ostream& out ) const
  40.     { out << _valor; }
  41.  
  42.   private:
  43.  
  44.     _TYPE_ _valor;
  45. };
  46.  
  47. class Lista
  48. {
  49.   public:
  50.  
  51.     Lista( )
  52.       : _primero( nullptr )
  53.     { }
  54.  
  55.     virtual ~Lista( )
  56.     {
  57.       while( _primero )
  58.       {
  59.         NodoBase* siguiente = _primero->Siguiente( );
  60.         delete _primero;
  61.         _primero = siguiente;
  62.       }
  63.     }
  64.  
  65.     template< class _TYPE_ >
  66.     void NuevoItem( _TYPE_ valor )
  67.     {
  68.       NodoBase* nuevoNodo = new Nodo<_TYPE_>( valor );
  69.  
  70.       if ( !_primero )
  71.         _primero = nuevoNodo;
  72.       else
  73.       {
  74.         NodoBase* nodo = _primero;
  75.         while( nodo->Siguiente( ) )
  76.           nodo = nodo->Siguiente( );
  77.  
  78.         nodo->SetSiguiente( nuevoNodo );
  79.       }
  80.     }
  81.  
  82.     void Pintar( std::ostream& out )
  83.     {
  84.       if ( !_primero )
  85.       {
  86.         out << "Lista vacía" << std::endl;
  87.         return;
  88.       }
  89.  
  90.       _primero->Pintar( out );
  91.       NodoBase* nodo = _primero->Siguiente( );
  92.       while( nodo )
  93.       {
  94.         out << " -> ";
  95.         nodo->Pintar( out );
  96.         nodo = nodo->Siguiente( );
  97.       }
  98.  
  99.       out << std::endl;
  100.     }
  101.  
  102.   private:
  103.  
  104.     NodoBase* _primero;
  105. };
  106.  
  107. int main( )
  108. {
  109.   Lista lista;
  110.  
  111.   lista.NuevoItem( 1 );
  112.   lista.NuevoItem( 'c' );
  113.   lista.NuevoItem( "prueba" );
  114.   lista.NuevoItem( 4.56 );
  115.  
  116.   lista.Pintar( std::cout );
  117. }

¿Por qué uso "NodoBase"?? por dos razones:

* Un template no puede tener un método virtual puro
* Las especializaciones de un template no tienen herencia común, class<int> no hereda, de ninguna forma, de class<T>, por lo que necesito un mecanismo que me permita incrustar tipos de "nodos" diferentes a partir de una clase común.

Un saludo.

Última edición por eferion; 15/04/2015 a las 07:44
  #10 (permalink)  
Antiguo 15/04/2015, 14:40
Avatar de vangodp  
Fecha de Ingreso: octubre-2013
Mensajes: 934
Antigüedad: 11 años, 1 mes
Puntos: 38
Respuesta: Diferenciar entrada de datos por consola

Cita:
vangodp, tu comentario me ha inspirado. Un ejemplo de una lista simple usando templates... la gracia está en que admite diferentes tipos de datos:
Hombre... Para lo que sirve si lo sé, pero lo que has puesto ahí de ejemplo me va tardar un año entenderlo jajajaj.

Lo voy a analizar detenidamente a ver si logro entenderlo XDDD. Ya te comento algo >_<.

Tu lo has echo con clases, yo lo vi templates en una estructura O.o. Voy a buscar el ejemplo a ver como estaba echo, lo debo tener guardado por algún lado. Pero desde luego no era así de complejo jajaj.
  #11 (permalink)  
Antiguo 16/04/2015, 00:18
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Diferenciar entrada de datos por consola

Cita:
Iniciado por vangodp Ver Mensaje
Hombre... Para lo que sirve si lo sé
No pretendía enseñarte para qué sirven... solo mostrarte un par de trucos a usar con los templates ;)

Cita:
Iniciado por vangodp Ver Mensaje
pero lo que has puesto ahí de ejemplo me va tardar un año entenderlo jajajaj.
Ya será menos.

La jerarquía de los nodos se compone de dos clases: NodoBase y Nodo<T>

NodoBase obliga a que las especializaciones de Nodo<T> implementen una serie de funciones (en el caso del ejemplo, sólo una). Este diseño te permite usar polimorfismo y usar todos los Nodo<T> (Nodo<int>, Nodo<float>, Nodo<string>...) como si fuesen un mismo tipo de objeto.

La gracia de este diseño es que podría almacenar elementos más complejos sin demasiado esfuerzo. En el siguiente ejemplo añado una especialización del template que almacena una estructura en vez de un tipo nativo:

Código C++:
Ver original
  1. #include <iostream>
  2. #include <string>
  3.  
  4. class NodoBase
  5. {
  6.   public:
  7.  
  8.     NodoBase( )
  9.       : _siguiente( nullptr )
  10.     { }
  11.  
  12.     virtual ~NodoBase( )
  13.     { }
  14.  
  15.     void SetSiguiente( NodoBase* siguiente )
  16.     { _siguiente = siguiente; }
  17.  
  18.     NodoBase* Siguiente( ) const
  19.     { return _siguiente; }
  20.  
  21.     virtual void Pintar( std::ostream& out ) const = 0;
  22.  
  23.   private:
  24.  
  25.     NodoBase* _siguiente;
  26. };
  27.  
  28. template< typename _TYPE_ >
  29. class Nodo : public NodoBase
  30. {
  31.   public:
  32.     Nodo( _TYPE_ valor )
  33.       : _valor( valor )
  34.     { }
  35.  
  36.     ~Nodo( )
  37.     { }
  38.  
  39.     virtual void Pintar( std::ostream& out ) const
  40.     { out << _valor; }
  41.  
  42.   private:
  43.  
  44.     _TYPE_ _valor;
  45. };
  46.  
  47. struct ObjetoComplejo
  48. {
  49.   std::string nombre;
  50.   int edad;
  51. };
  52.  
  53. template< >
  54. class Nodo< ObjetoComplejo > : public NodoBase
  55. {
  56.   public:
  57.     Nodo( ObjetoComplejo valor )
  58.       : _valor( valor )
  59.     { }
  60.  
  61.     ~Nodo( )
  62.     { }
  63.  
  64.     virtual void Pintar( std::ostream& out ) const
  65.     { out << _valor.nombre << "(" << _valor.edad << ")"; }
  66.  
  67.   private:
  68.  
  69.     ObjetoComplejo _valor;
  70. };
  71.  
  72. class Lista
  73. {
  74.   public:
  75.  
  76.     Lista( )
  77.       : _primero( nullptr )
  78.     { }
  79.  
  80.     virtual ~Lista( )
  81.     {
  82.       while( _primero )
  83.       {
  84.         NodoBase* siguiente = _primero->Siguiente( );
  85.         delete _primero;
  86.         _primero = siguiente;
  87.       }
  88.     }
  89.  
  90.     template< class _TYPE_ >
  91.     void NuevoItem( _TYPE_ valor )
  92.     {
  93.       NodoBase* nuevoNodo = new Nodo< _TYPE_ >( valor );
  94.  
  95.       if ( !_primero )
  96.         _primero = nuevoNodo;
  97.       else
  98.       {
  99.         NodoBase* nodo = _primero;
  100.         while( nodo->Siguiente( ) )
  101.           nodo = nodo->Siguiente( );
  102.  
  103.         nodo->SetSiguiente( nuevoNodo );
  104.       }
  105.     }
  106.  
  107.     void Pintar( std::ostream& out )
  108.     {
  109.       if ( !_primero )
  110.       {
  111.         out << "Lista vacía" << std::endl;
  112.         return;
  113.       }
  114.  
  115.       _primero->Pintar( out );
  116.       NodoBase* nodo = _primero->Siguiente( );
  117.       while( nodo )
  118.       {
  119.         out << " -> ";
  120.         nodo->Pintar( out );
  121.         nodo = nodo->Siguiente( );
  122.       }
  123.  
  124.       out << std::endl;
  125.     }
  126.  
  127.   private:
  128.  
  129.     NodoBase* _primero;
  130. };
  131.  
  132. int main( )
  133. {
  134.   Lista lista;
  135.  
  136.   lista.NuevoItem( 1 );
  137.   lista.NuevoItem( 'c' );
  138.   lista.NuevoItem( "prueba" );
  139.   lista.NuevoItem( 4.56 );
  140.  
  141.   ObjetoComplejo complejo;
  142.   complejo.nombre = "pepe";
  143.   complejo.edad = 31;
  144.  
  145.   lista.NuevoItem( complejo );
  146.  
  147.   lista.Pintar( std::cout );
  148. }

Luego, lo que hace la clase "Lista" es almacenar una lista simple de elementos de tipo "NodoBase". La función que permite añadir nuevos elementos es un template, de tal forma que la especialización de la función es crear el nodo específico para el tipo T (por ejemplo, la especialización Pintar<int> es capaz de crear Nodo<int>). Esto quiere decir que, a la hora de la verdad, la función "Pintar" estará sobrecargada, existiendo una versión para cada tipo de nodo que deseemos añadir.

Una vez los nodos forman parte de la lista, el objeto "Lista" es capaz de interactuar con ellos haciendo uso de los métodos virtuales de "NodoBase". De esta forma puede, como he comentado antes, acceder a ciertas características de los nodos s
  #12 (permalink)  
Antiguo 16/04/2015, 06:35
Avatar de vangodp  
Fecha de Ingreso: octubre-2013
Mensajes: 934
Antigüedad: 11 años, 1 mes
Puntos: 38
Respuesta: Diferenciar entrada de datos por consola

-_-' Joer eferion. Me gustaría entender ese código.
Tiene pinta de ser muy potente el tema, pero al no tener demasiada exp con plantillas me va resultar difícil.

Lo voy a guardar bajo llaves y profundizar un poco sobre plantillas.

El código que tenia guardado es este: http://codepad.org/EzdsF9jP

luego en la función main puedes enviar otras estructuras a esos nodos. Lista<Equipo> equipos; por ejemplo. No se muy bien como trabaja todo pero me parecio muy muy interesante. XD


Luego ya juntare todo a ver que saco de esto =).
Me parece muy interesante eso de las plantillas. Cuando pueda, pondré tiempo al tema.

Gracias por la explicación. Ya te comentare algo luego. =))

Suerte!

Última edición por vangodp; 16/04/2015 a las 06:45
  #13 (permalink)  
Antiguo 16/04/2015, 09:27
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 10 años, 4 meses
Puntos: 1
Respuesta: Diferenciar entrada de datos por consola

Hola,

Gracias a todos especialmente a vangodp y a eferion, no había comentado antes pues estaba estudiando el código jeje, ya he entendido lo de identificar si es un número o no con templates o bien con la función isdigit (que por cierto hay otra con el mismo nombre pero que solo tiene un parámetro).

Aprovechando que ya hablaron de otro tema, para estudiar el código me gustaría saber que es lo que dijo vangodp y luego programo eferion "un nodo de una lista simple de una estructura con plantillas" pues no me ha quedado muy claro que es eso jeje.

saludos
  #14 (permalink)  
Antiguo 16/04/2015, 10:25
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 10 años, 4 meses
Puntos: 1
Respuesta: Diferenciar entrada de datos por consola

Una cosa más? exactamente que es eso de "invalid_argument"? sé que es una clase pero no encuentro ejemplos claros en internet

saludos
  #15 (permalink)  
Antiguo 16/04/2015, 13:34
Avatar de vangodp  
Fecha de Ingreso: octubre-2013
Mensajes: 934
Antigüedad: 11 años, 1 mes
Puntos: 38
Respuesta: Diferenciar entrada de datos por consola

Cita:
"un nodo de una lista simple de una estructura con plantillas"
Algo así. La idea es hacer un nodo que acepte cualquier tipo de datos o casi. >_<

Etiquetas: char, consola, diferenciar, entrada, int, programa
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 10:11.