Hola gente, estuve codeando un .h, el cual previene que cuando esperamos un valor de tipo integer y nos ingresan un valor de tipo string no nos tire error, y nos cierre el programa, les dejo un video, y el .h abajo, con un main.cpp para ejemplo de su uso de datos.
Main.cpp
Código
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
#include <string>
using std::string;
using std::getline;
#include "fixedInteger.h"
int main()
{
string numero;
cout<<"numero: ";
getline(cin,numero);
cout<<endl;
fixedInteger aplicacion(numero);
cin.get();
return0;
}
fixedInteger.h
Código
/*
<<<<<<<<<<<FIXED INTEGER>>>>>>>>>>>>>>>>
Creado por: Mario Olivera
MISION:
•Solucionar el error de cuando esperamos un integer y nos dan un string :)
USO:
•Use un objeto desde un .cpp ejemplo: "fixedInteger objeto(una variable string);"
• Para obtener el numero integer, solo ponga "objeto.obtenerInteger();" ejemplo: int numero= objeto.obtenerInteger();
Atencion: si usted esta esperando un numero integer positivo podria poner un if en su .cpp, ejemplo: si el numero es negativo
entonces ejecuto de nuevo las funciones de fixedInteger;
400 lineas para convertir un número de string a int es excesivo ·_·
Luego, obviando el uso abusivo de if-else, ten en cuenta que estás comparando strings de 1 caracter. Es más, llamas a una variable string "caracter". Puedes y deberías usar el tipo char.
PD: También existe la función stoi de la librería string (stoi, stoul, stof, ...)
throw domain_error("Hey!!! Eso no es un numero!!");
char c;cin>> c;
if(!cin.fail())
throw domain_error("Hey!!! Has escrito algo mas detras del numero!!");
return numero;
}
int main()
{
while(true){// Hasta que el numero no sea correcto, no te escapas.
try{
cout<< getNumero()<< endl;
break;
}catch(domain_error const& e){
cerr<< e.what()<< endl;
}
}
return0;
}
Si estás empezando con C++ y te quedas un poco a cuadros con éste código (nadie ha dicho que C++ fuera fácil), pues ve investigando por Internet una a una las funciones y operadores que he utilizado aquí para que lo vayas desgranando, pero fundamentalmente, lo que veo que has malinterpretado es cómo se comportan los flujos en C++ (cin). Ellos ya hacen un montón de trabajo de parseo por tí.
Consejo: no reinventes la rueda. Cada vez que veas algo que sea simple, pero que implique mucho trabajo, seguramente ya esté hecho en algún lugar.
Mod: etiqueta GeSHi modificada, debe ser code=cpp, no code=c++
« Última modificación: 19 Mayo 2015, 09:09 am por engel lex »
y si el numero ingresado es "0" o "0000000"?... son números validos
« Última modificación: 19 Mayo 2015, 09:14 am por engel lex »
En línea
El problema con la sociedad actualmente radica en que todos creen que tienen el derecho de tener una opinión, y que esa opinión sea validada por todos, cuando lo correcto es que todos tengan derecho a una opinión, siempre y cuando esa opinión pueda ser ignorada, cuestionada, e incluso ser sujeta a burla, particularmente cuando no tiene sentido alguno.
Si nos ponemos pedantes, ese código de Gunhack sufre dos defectos:
La operación de obtener el número y la de notificar al usuario del error y el reintento, están acoplados.
¿Y si mañana quisieses cambiar el tipo de número a `unsigned`, `long long int` o `long double`?
En mi caso, bastaría con:
Código
#include <iostream>
#include <stdexcept>
usingnamespace std;
#define READ_TYPE int
template<typename Num_t>
Num_t getNumero()
{
Num_t numero;cin>> numero;
// A partir de aquí, ya no hay más referencias al tipo.
if(cin.fail())
throw domain_error("Hey!!! Eso no es un numero!!");
char c;cin>> c;
if(!cin.fail())
throw domain_error("Hey!! Has escrito algo mas detras del numero!!");
return numero;
}
int main()
{
while(true){// Hasta que el número no sea correcto, no te escapas.
try{
cout<< getNumero<READ_TYPE>()<< endl;
break;
}catch(domain_error const& e){
cerr<< e.what()<< endl;
}
}
return0;
}
Y el tipo que deseo obtener lo puedo especificar pasándole el valor de la macro al compilador (con la opción `-Dmacro=valor` en `gcc` si no me equivoco), así que no tengo que tocar el código si quiero cambiar el tipo.
En el caso de Gunhack, habría que cambiar el tamaño del búfer, y eso no es una tarea tan sencilla: ¿cuántos dígitos requiere un `long int`? http://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models Seguramente con metaprogramación, `constexpr`s y la librería `<climits>` se pueden obtener buenas cotas, pero ¿y qué pasa con números flotantes? En fin...
La ventaja del código de Gunhack, por otro lado, es que en caso error, su alternativa es claramente menos costosa que la basada en excepciones, pero a veces hay que sacrificar eficiencia a cambio de diseño.
Aunque bueno, éstas consideraciones se escapan al objetivo del ejercicio de Mario, pero he dicho al inicio de la respuesta que estamos en pedantic mode.
« Última modificación: 19 Mayo 2015, 16:34 pm por Peregring-lk »
Ok. Aquí el mío, con las comprobaciones siguientes:
Detección de signo '+' (línea 34).
Escape de prefijo de 0s (línea 37).
Detección de número demasiado grande capturando desbordamiento (línea 15).
Paramétrico: acepta cualquier tipo númerico, sin importar si es signed o unsigned, ni su tamaño (short, long, long long), ni los detalles de la arquitectura: si la arquitectura utiliza complemento a dos para representar números, por ejemplo para el caso `sizeof(int) == 4`, el rango va desde -2^31 hasta 2^31-1, pero si utiliza complemento a uno, va desde -2^31+1 hasta 2^31-1, para números sin signo.
Si se desea un `unsigned`, pero se ha introducido el signo `-`, también lanza error (línea 31).
He hecho la función recursiva para ahorrar líneas (sustituir asignaciones por parámetros). No me gusta poner en foros códigos muy largos. De todas formas, el compilador seguramente la cambiará por una función iterativa sin mucho esfuerzo.
return getNum(str, next, sign, i +1, str[i +1]-'0');
}
template<typename Num_t>
Num_t getNum(string const& str)
{
if(str.size()==0)throw invalid_argument("Introduzca un numero.");
size_t i =0;
signedchar sign =1;
if(str[i]=='-'){
if(is_unsigned<Num_t>::value)
throw invalid_argument("Un numero natural no puede ser < 0.");
++i; sign =-1;
}elseif(str[i]=='+')
++i;
while(str[i]=='0')++i;// Ignoramos prefijo de 0s.
return getNum<Num_t>(str, 0, sign, i, str[i]-'0');
}
int main()
{
while(cin.good())// Hasta que no me des un numero (y quede entrada) no te escapas.
try{
string str;
getline(cin, str);cin.ignore();
cout<< getNum<unsigned>(str)<< endl;
break;
}catch(invalid_argument const& e){
cerr<< e.what()<< endl;
}
return0;
}
Nota sobre chars: Mientras que el tipo `char` se utiliza para manipular carácteres, los tipos `signed char` y `unsigned char` se utilizan para manipular números de 1 byte (si `char` tiene signo o no, se deja a criterio del compilador). Si queréis utilizar éste programa para capturar números en el rango [-127, 127], [-128, 127] o [0, 255] (ya sabéis, números de 1 byte, signed/unsigned), la línea 48 no imprime el número, sino el carácter correspondiente devuelto por `getNum`. Tenéis que hacer un casting en ese caso a `int` por ejemplo para imprimir el número.
Aunque por supuesto, siempre hay solución para ello, sin tener que hacer casting manual
Código
// Alias de `std::conditional`, para que `char2Short` y `RetNum` no queden muy largos.
template<bool b, typename T, typename F>
using conditional_t =typename conditional<b, T, F>::type;
// Transformacion char -> short conservando signo.
template<typename T>
using char2Short = conditional_t<is_signed<T>::value, short, unsignedshort>;
// Nos aseguramos de devolver al menos un short, para que el
// numero se imprima bien.
template<typename T>
using RetNum = conditional_t<sizeof(T)==1, char2Short<T>, T>;
// Sobrecarga recursiva de `getNum` sin cambios, ya que si devolviese un `short` cuando
// estamos trabajando con `char`s, podría fallar la comprobación de desbordamiento (no
// estoy seguro en realidad).
template<typename Num_t>
RetNum<Num_t> getNum(string const& str)
{/* Implementacion */}
// Main con getNum<char>(str) o getNum<signed char>(str), por ejemplo ...
« Última modificación: 19 Mayo 2015, 23:27 pm por Peregring-lk »