Test Foro de elhacker.net SMF 2.1

Programación => Programación C/C++ => Mensaje iniciado por: Mario Olivera en 16 Mayo 2015, 23:15 PM

Título: [Aporte] fixedInteger.h
Publicado por: Mario Olivera en 16 Mayo 2015, 23:15 PM
 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;

};
Título: Re: [Aporte] fixedInteger.h
Publicado por: ivancea96 en 17 Mayo 2015, 01:48 AM
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, ...)
Título: Re: [Aporte] fixedInteger.h
Publicado por: Peregring-lk en 17 Mayo 2015, 02:16 AM
¿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++
Título: Re: [Aporte] fixedInteger.h
Publicado por: Gunhack en 19 Mayo 2015, 04:50 AM
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;
}

Título: Re: [Aporte] fixedInteger.h
Publicado por: engel lex en 19 Mayo 2015, 09:08 AM
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
Título: Re: [Aporte] fixedInteger.h
Publicado por: Peregring-lk en 19 Mayo 2015, 16:23 PM
Si nos ponemos pedantes, ese código de Gunhack sufre dos defectos:


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  (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.
Título: Re: [Aporte] fixedInteger.h
Publicado por: ivancea96 en 19 Mayo 2015, 16:39 PM
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;
}
Título: Re: [Aporte] fixedInteger.h
Publicado por: Peregring-lk en 19 Mayo 2015, 22:33 PM
Ok. Aquí el mío, con las comprobaciones siguientes:


Código en coliru: http://coliru.stacked-crooked.com/a/bf7bfac2d1b46842 (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 (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 ...