[Aporte] fixedInteger.h

Iniciado por Mario Olivera, 16 Mayo 2015, 23:15 PM

0 Miembros y 1 Visitante están viendo este tema.

Mario Olivera

 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.

[youtube=640,360]https://www.youtube.com/watch?v=tnJqZjGz23A[/youtube]


Main.cpp

Código (cpp) [Seleccionar]
#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();
return 0;
}


fixedInteger.h


Código (cpp) [Seleccionar]
/*
<<<<<<<<<<<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;

Nada Mas que decir, saludos! Mario Olivera.
*/



/////////////////////////////////////////////CODIGO ////////////////////////////////////////

#include <iostream>
using std::cout;
using std::endl;
using std::cin;

#include <string>
using std::string;

class fixedInteger
{
public:
fixedInteger(string numero)
{
convertidor(numero);
}

int obtenerInteger()
{
return numeroInteger;
}




void convertidor(string numero)
{
int ladoDelCuadrado=0;
int cantidadDeCaracteres;
int resultado=1;
int i=1;
string auxNumero= numero;
string caracter;
string primerCaracter;
string aux;
bool interruptor= false;
bool negativo= false;


cantidadDeCaracteres= (auxNumero.length());

auxNumero= "f" + auxNumero;

while (interruptor == false)
{
interruptor= true;
if (cantidadDeCaracteres == 1)
{
caracter= auxNumero[1];

if (caracter == "0")
{
ladoDelCuadrado= 0;
}
else
{
if (caracter == "1")
{
ladoDelCuadrado= 1;
}
else
{
if (caracter == "2")
{
ladoDelCuadrado= 2;
}
else
{
if (caracter == "3")
{
ladoDelCuadrado= 3;
}
else
{
if (caracter == "4")
{
ladoDelCuadrado= 4;
}
else
{
if (caracter == "5")
{
ladoDelCuadrado= 5;
}
else
{
if (caracter == "6")
{
ladoDelCuadrado= 6;
}
else
{
if (caracter == "7")
{
ladoDelCuadrado= 7;
}
else
{
if (caracter == "8")
{
ladoDelCuadrado=8;
}
else
{
if (caracter == "9")
{
caracter= 9;
}
else
{
interruptor= false;
}
}
}
}
}
}
}
}
}
}

}



else
{
primerCaracter= auxNumero[1];
aux= auxNumero[1];
while (i < cantidadDeCaracteres)
{

if ((aux == "+") || (aux == "-"))
{

}
else
{
resultado*= 10;
}
++i;
}
i= 1;

caracter= auxNumero[i];

if (caracter == "+")
{
i++;
}
if (caracter == "-")
{
i++;
negativo= true;
}

while (i < cantidadDeCaracteres)
{
caracter= auxNumero[i];
if (caracter == "0")
{

if ((primerCaracter != "+") || (primerCaracter != "-"))
{
if (i > 1)
{
ladoDelCuadrado= ladoDelCuadrado + ( 0 * resultado);
}

}
else
{
if (i > 2)
{
ladoDelCuadrado= ladoDelCuadrado + ( 0 * resultado);
}
}
resultado/= 10;
}
else
{
if (caracter == "1")
{
ladoDelCuadrado= ladoDelCuadrado + ( 1 * resultado);
resultado/= 10;
}
else
{
if (caracter =="2")
{
ladoDelCuadrado= ladoDelCuadrado + ( 2 * resultado);
resultado/= 10;
}
else
{
if (caracter == "3")
{
ladoDelCuadrado= ladoDelCuadrado + ( 3 * resultado);
resultado/= 10;
}
else
{
if (caracter == "4")
{
ladoDelCuadrado= ladoDelCuadrado + ( 4 * resultado);
resultado/= 10;
}
else
{
if (caracter == "5")
{
ladoDelCuadrado= ladoDelCuadrado + ( 5 * resultado);
resultado/= 10;
}
else
{
if (caracter == "6")
{
ladoDelCuadrado= ladoDelCuadrado + ( 6 * resultado);
resultado/= 10;
}
else
{
if (caracter == "7")
{
ladoDelCuadrado= ladoDelCuadrado + ( 7 * resultado);
resultado/= 10;

}
else
{
if (caracter == "8")
{
ladoDelCuadrado= ladoDelCuadrado + ( 8 * resultado);
resultado/= 10;
}
else
{
if (caracter == "9")
{
ladoDelCuadrado= ladoDelCuadrado + ( 9 * resultado);
resultado/= 10;
}
else
{
interruptor= false;
}
}
}
}
}
}
}

}
}


}
++i;


}

}

if (cantidadDeCaracteres != 1)
{
caracter= auxNumero.substr(2,cantidadDeCaracteres);

if ((caracter != "0") && (caracter != "1") && (caracter != "2")  && (caracter != "3")  && (caracter != "4")  && (caracter != "5")  && (caracter != "6")  && (caracter != "7")  && (caracter != "8")  && (caracter != "9"))
{
interruptor= false;
}

if (interruptor == true)
{
caracter= auxNumero.substr(cantidadDeCaracteres);

if (caracter == "1")
{
ladoDelCuadrado= ladoDelCuadrado + 1;
}
else
{
if (caracter == "2")
{
ladoDelCuadrado= ladoDelCuadrado + 2;
}
else
{
if (caracter == "3")
{
ladoDelCuadrado= ladoDelCuadrado + 3;
}
else
{
if (caracter == "4")
{
ladoDelCuadrado= ladoDelCuadrado + 4;
}
else
{
if (caracter == "5")
{
ladoDelCuadrado= ladoDelCuadrado + 5;
}
else
{
if (caracter == "6")
{
ladoDelCuadrado= ladoDelCuadrado + 6;
}
else
{
if (caracter == "7")
{
ladoDelCuadrado= ladoDelCuadrado + 7;
}
else
{
if (caracter == "8")
{
ladoDelCuadrado= ladoDelCuadrado + 8;
}
else
{
ladoDelCuadrado= ladoDelCuadrado + 9;
}
}
}
}
}
}
}
}

}
else
{

cout <<"ingrese el valor nuevamente: ";
getline(cin, auxNumero);

ladoDelCuadrado=0;
resultado=1;
i=1;
interruptor= false;
cantidadDeCaracteres= (auxNumero.length());
auxNumero= "f" + auxNumero;
}

}
else
{
if (interruptor == false)
{
cout <<"ingrese el valor nuevamente: ";
getline(cin, auxNumero);

ladoDelCuadrado=0;
resultado=1;
i=1;
interruptor= false;
cantidadDeCaracteres= (auxNumero.length());
auxNumero= "f" + auxNumero;
}
}
}

if (negativo == true)
{
ladoDelCuadrado= ladoDelCuadrado * -1;
}

numeroInteger= ladoDelCuadrado;
}

private:
string mensajeError;
int numeroInteger;

};

ivancea96

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, ...)

Peregring-lk

#2
¿Excesivo? Te quedas corto xD.

Es mucho más fácil y corto así:

Código (cpp) [Seleccionar]

#include <iostream>
#include <stdexcept>

using namespace std;

int getNumero()
{
  int numero; cin >> numero;

  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 numero no sea correcto, no te escapas.
    try {
         cout << getNumero() << endl;
         break;
    } catch (domain_error const& e) {
        cerr << e.what() << endl;
    }
  }

  return 0;
}


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++

Gunhack

Se me hace más fácil esto  ;D
Código (cpp) [Seleccionar]

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

using namespace std;

int main()
{
  char datos[21];
  int x;

  do{
    cout << "Ingresa un numero: ";
    cin.getline(datos,20);
    x=atoi(datos);
    if (x==0)
      cout << "\nSolo ingresa numeros!!!\n\n";
  }while(x==0);

  cout <<"El numero que ingresaste es: "<< x;
  return 0;
}


engel lex

#4
Cita de: Gunhack en 19 Mayo 2015, 04:50 AM
Se me hace más fácil esto  ;D


y si el numero ingresado es "0" o "0000000"?... son números validos
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.

Peregring-lk

#5
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 (cpp) [Seleccionar]

#include <iostream>
#include <stdexcept>

using namespace 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;
    }
  }

  return 0;
}


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.

ivancea96

#6
Con todo el respeto, de los códigos que se han aportado hasta ahora, ninguno tiene que ver con el post original.

El post original son métodos que convierten una string a un número, sin usar funciones de otras librerías con esa habilidad.

Al menos, proponed códigos que conviertan una cadena de caracteres en un entero :/

Edito:

Código (cpp) [Seleccionar]
#include <iostream>
#include <cmath>
#include <stdexcept>

using namespace std;

bool lessOrEqual(string a, string b){
    if(a.size()<b.size()) return true;
    if(a.size()>b.size()) return false;
    if(a==b) return true;
    for(int i=0; i<a.size(); i++)
        if(a[i]<b[i]) return true;
        else if(a[i]>b[i]) return false;
    return true;
}

int toInt(string str){
    bool sign = str[0] == '-';

    for(int i=sign; i<str.size(); i++)
        if(str[i]<'0' || str[i]>'9')
            throw invalid_argument("Not numeric character found");

    size_t len = str.size()-sign;
    if(sign)
        str.erase(0,1);

    if(sign){
        if(!lessOrEqual(str, "2147483648"))
            throw out_of_range("Limits: [-2147483648, 2147483647]");
    }else
        if(!lessOrEqual(str, "2147483647"))
            throw out_of_range("Limits: [-2147483648, 2147483647]");

    int temp = 0;
    for(int i=str.size()-1; i>=0; i--)
        temp += (str[i]-'0')*pow(10, str.size()-1-i);
    if(sign)
        temp *= -1;
    return temp;
}

int main(){
    int number;
    while(1){
        cout << "Enter a number: ";
        string temp;
        getline(cin,temp);
        bool good = true;
        try{
            number = toInt(temp);
            break;
        }catch(exception& e){
            endl(cerr << "Exception: " << e.what());
            endl(cout << "Invalid input.");
        }
    }
    cout << "The number is: " << number;
}

Peregring-lk

#7
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.

Código en coliru: http://coliru.stacked-crooked.com/a/bf7bfac2d1b46842 (haced click en `Edit` y cambiar la entrada por el número que queráis para probar el programa).

Aquí una lista de los límites numéricos según la arquitectura (sección Range of values): http://en.cppreference.com/w/cpp/language/types, por si queréis hacer pruebas.

Para cambiar el tipo que deseáis obtener, cambiad el que aparece en la línea 48: `getNum<tipo>(str)`.

Código (cpp) [Seleccionar]

#include <iostream>
#include <stdexcept>
#include <type_traits>

using namespace std;

template<typename Num_t> // Funcion recursiva, caracter a caracter.
Num_t getNum(string const& str, Num_t num, signed char sign, size_t i, unsigned char nextc)
{
   if (i == str.size()) return num;
   if (nextc > 9) throw invalid_argument("Eso no es un numero.");
   
   Num_t next = num * 10 + sign * nextc;
   
   if (sign == 1 ? (next < num) : (next > num))
       throw invalid_argument("Numero demasiado grande.");
   
   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;
   signed char sign = 1;
   
   if (str[i] == '-') {
       if (is_unsigned<Num_t>::value)
           throw invalid_argument("Un numero natural no puede ser < 0.");
           
       ++i; sign = -1;
   } else if (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;
       }
   
   return 0;
}



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 (cpp) [Seleccionar]

// 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, unsigned short>;

// 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 ...