Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: patilanz en 7 Marzo 2014, 23:57 pm



Título: Recibir int seguro
Publicado por: patilanz en 7 Marzo 2014, 23:57 pm
Hola he buscado por internet como recibir valores por internet de tal manera que sea int y si es un bucle que comprueba los datos no se pare si se inserta una letra.
De modo que este codigo no funciona:

Código
  1. int d;
  2. do{
  3. cout << msg;
  4. cin >> d;
  5. cin.clear();
  6. //while(cin.get()!='\n');
  7. }while(cin.fail());
  8. return d;

Como comprobar un int?

Saludos


Título: Re: Recibir int seguro
Publicado por: rir3760 en 8 Marzo 2014, 02:10 am
Esa pregunta ya se ha respondido en varias ocasiones en los foros, solo tienes que utilizar el motor de búsqueda para encontrar los temas relacionados. Por ejemplo Validar algunas cosas en C++ (http://foro.elhacker.net/programacion_cc/validar_algunas_cosas_en_c-t404359.0.html).

Un saludo


Título: Re: Recibir int seguro
Publicado por: leosansan en 8 Marzo 2014, 07:00 am

El método que propones tiene un pero, si introduces 12as toma 12 como int.

Vero más seguro elñ siguiente código, y aunque está en C no creo que patilanz tenga problema en adaptarlo a C++:


Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <ctype.h>
  4. #define MAX_DIGITOS  100
  5.  
  6. int comprobar_cadena_como_numero (char numero[MAX_DIGITOS]);
  7.  
  8. int main(){
  9.  
  10.  char cadena[MAX_DIGITOS];
  11.  do{
  12.    printf("\nIntroduce un numero entero: ");
  13.    scanf("%s",&cadena);
  14.    }while (comprobar_cadena_como_numero(cadena)!=2);
  15.    printf("\nentero: %d\n",atoi (cadena));
  16.    return EXIT_SUCCESS;
  17. }
  18.  
  19. int comprobar_cadena_como_numero (char cadena[MAX_DIGITOS]){
  20.    int i;
  21.    for (i=0;cadena[i];i++)
  22.      if (!isdigit(cadena[i]))
  23.            return 1;
  24.    return 2;
  25. }

¡¡¡¡ Saluditos! ..... !!!!


(http://st.forocoches.com/foro/images/smilies/aaaaa.gif)


Título: Re: Recibir int seguro
Publicado por: patilanz en 8 Marzo 2014, 15:19 pm
Gracias ahora consegui hacer lo.

Código
  1. int getInt(string msg){
  2. string d;
  3. do{
  4. cout << msg;
  5. cin >> d;
  6. }while(!isInt(d));
  7. return atoi(d.data());
  8. }
  9.  
  10.  
  11. int isInt(string &str){
  12. for(size_t i=0;i<str.size();i++){
  13. if(!isdigit(str[i])){
  14. return 0;
  15. }
  16. }
  17. return 1;
  18. }

Pero una pregunta. El código de c que utiliza char[] no daría error si se introduce un valor mayor que el size?
Muchas gracias


Título: Re: Recibir int seguro
Publicado por: leosansan en 8 Marzo 2014, 16:00 pm
Gracias ahora consegui hacer lo.
.......................................................
Pero una pregunta. El código de c que utiliza char[] no daría error si se introduce un valor mayor que el size?
Muchas gracias

¿Y quién es el animalito que al pedirle un entero introduce más de 100 dígitos?.

Claro que haberlos "haylos" pero todo sería cuestión de hacer una limpieza adicional del buffer.


¡¡¡¡ Saluditos! ..... !!!!


(http://st.forocoches.com/foro/images/smilies/aaaaa.gif)


Título: Re: Recibir int seguro
Publicado por: patilanz en 8 Marzo 2014, 16:18 pm
Me refiero a insertar código desde fuera sustituyendo la memoria cuando se acabe el buffer. O no es posible?

Con una clase que contiene cadenas con memoria dinamica esto no pasa y en este caso ? con char[size] ??


Título: Re: Recibir int seguro
Publicado por: Yoel Alejandro en 8 Marzo 2014, 18:19 pm
Claro, el "desbordamiento de buffer" es un problema típico en la programación, y que por supuesto debemos evitar.

Para C (en C++ el tamaño del arreglo se ajusta automáticamente) una solución sería usar fgets(char *s, int size, FILE *stream), que limita la cantidad de caracteres leídos a size - 1 (se resta uno porque la última posición está reservada para el nulo de terminación). En stream hemos de poner stdin para que lea de la entrada estándar (hay unas consideraciones con fgets() que explicaré al final)

Código
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <ctype.h>
  5.  
  6. int getInt(char *);
  7. int isInt(char * s);
  8.  
  9. int main( ) {
  10.  
  11. int x;
  12. char msg[] = "Intro numero: ";
  13.  
  14. printf("Numero es: %d\n", getInt(msg));
  15. }
  16.  
  17. int getInt(char *msg) {
  18.  
  19. #define N 3
  20. char s[N + 1];
  21. int i;
  22. char clean_return;
  23.  
  24. do {
  25. printf("%s", msg);
  26. fgets(s, N + 1, stdin);
  27.  
  28. /* reemplazar '\n' (si se encuentra) por '\0' */
  29. i = -1;
  30. clean_return = 1;
  31. while ( s[++i] != '\0' )
  32. if ( s[i] == '\n' ) {
  33. s[i] = '\0';
  34. clean_return = 0;
  35. break;
  36. }
  37.  
  38. /* si no se pasó '\n' al arreglo, limpiarlo del bufer de entrada */
  39. if ( clean_return ) getchar();
  40. } while( *s == '\0' || !isInt(s) );
  41.  
  42. return atoi(s);
  43. }
  44.  
  45. int isInt(char * s) {
  46.  
  47. int i = -1;
  48.  
  49. while ( s[++i] != '\0' ) {
  50. if( !isdigit(s[i]) )
  51. return 0;
  52. }
  53. return 1;
  54. }

El programa pide al usuario un cadena, que es evaluada como entero siempre y cuando todos sus caracteres sean numéricos. De lo contrario, pide de nuevo. Al final imprime lo que leyó, para verificar.

Como ya se explicó, si por ejemplo declaras un arreglo char[] de N+1 caracteres, entonces fgets() lee como mucho N caractares. Si el fin de línea se encuentra dentro de esos N caracteres (si la cadena tiene una longitud menor N) entonces el fin de linea '\n' es pasado al arreglo. De lo contrario, (si la longitud de la cadena es mayor o igual a N) el '\n' no se pasa al arreglo y queda "pendiente" en el flujo de entrada del teclado. Por eso es necesario eliminarlo con getchar()

Se podría hacer una modificación al isInt() con el objeto de admitir un único carácter de signo '-' al inicio de la cadena, así permitir números negativos.

El código propuesto está en C, lo que implementa una solución más "básica" que C++. Creo que una simple operación de lectura de datos por teclado no exige invocar el complejo modelo de objetos de E/S de C++ ..... (bueno aunque de ahí en adelante no opino más, ello depende del gusto del programador)


Título: Re: Recibir int seguro
Publicado por: ivancea96 en 8 Marzo 2014, 18:30 pm
Código
  1.    //Dada una string 's'...
  2.    /** INT && UINT **/
  3.    bool sign=false;
  4.    if(s[0]=='-')
  5.        sign=true;
  6.    if(allNumeric(s.substr(sign, s.size()))){
  7.        if(sign){
  8.            if(minorEqual(s.substr(1, s.size()), "2147483648"))
  9.                type+=type_int;
  10.        }else{
  11.            if(minorEqual(s.substr(0, s.size()), "2147483647"))
  12.                //Se puede convertir a INT
  13.            if(minorEqual(s.substr(0, s.size()), "4294967295"))
  14.                //Se puede convertir a Unsigned INT
  15.        }
  16.    }
  17.  


Título: Re: Recibir int seguro
Publicado por: leosansan en 8 Marzo 2014, 19:16 pm
Claro, el "desbordamiento de buffer" es un problema típico en la programación, y que por supuesto debemos evitar.

Para C (en C++ el tamaño del arreglo se ajusta automáticamente) una solución sería usar fgets
...................................

Tiene una pequeñita pega el "rollo" de código que empleas:

* Si introduces 12 te lo da como bueno y ¿no era de tres dígitos el número a introducir?.

* Si introduces 123456 te da como bueno el 123 y ¿no es posible que se hallan equivocado al introducir los tres primeros dígitos?.

Reconozco que mi propuesta no es tan completa y compleja como la que propone ivancea96 pero sirve para estos casos de "andar por casa" y para hacerlo todo a "pelo" calculo hasta la longitud con mi propia función, más que nada por variar ya que antes utilice isdigit:


Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAX_DIGITOS  100
  4.  
  5. int comprobar_cadena_como_numero (char numero[MAX_DIGITOS]);
  6. int longitud_cadena (char cadena[MAX_DIGITOS]);
  7. /****************************************************/
  8. int main(){
  9.  
  10.  char cadena[MAX_DIGITOS];
  11.  do{
  12.    printf("\nIntroduce un numero entero de cinco digitos: ");
  13.    scanf("%s",&cadena);
  14.    while (getchar()!='\n');
  15.    }while (comprobar_cadena_como_numero(cadena)!=2 || longitud_cadena(cadena) !=5);
  16.    printf("\nentero: %d\n",atoi (cadena));
  17.    return EXIT_SUCCESS;
  18. }
  19. /****************************************************/
  20. int comprobar_cadena_como_numero (char cadena[MAX_DIGITOS]){
  21.    int i;
  22.    for (i=0;cadena[i];i++)
  23.       if (cadena[i]<'0' || cadena[i]>'9')
  24.            return 1;
  25.    return 2;
  26. }
  27.  
  28. /****************************************************/
  29. int longitud_cadena (char cadena[MAX_DIGITOS]){
  30.    int i;
  31.    for (i=0;cadena[i];i++);
  32.    return i;
  33. }
  34. /****************************************************/
  35.  

Ya sé que tiene una pequeña pega pero este código sólo admite cindo dígitos, ni uno más ni uno menos.

Y todo esto es para pasar el tiempo y compartir conocimientos. Yo sigo aprendiendo de todos ustedes. Gracias.


¡¡¡¡ Saluditos! ..... !!!!


(http://st.forocoches.com/foro/images/smilies/aaaaa.gif)


Título: Re: Recibir int seguro
Publicado por: Yoel Alejandro en 9 Marzo 2014, 17:26 pm
Ah, leosansan pero es mi intención fue precisamente recibir una cadena de 3 o menos dígitos. O sea, si aceptamos en forma estricta la longitud y el número fuera 12 tendrías que escribir 012, que es un fastidio, puff

Una solución sería definir un búfer más grande (por ello la constante N), digamos de 10 o 20 caracteres, así el usuario dispone de todos esos espacios para escribir su número (grande o pequeño). Ahora si él se equivoca tecleado el número, es culpa de él, jajajajajajaja

La otra opción de que sea una cantidad fija de dígitos, ni más ni menos, también puede ser deseable. Eso depende de cómo el diseñador quiera presentar la interfaz de su programa, y de la aplicación que involucre. Si por ejemplo vas a meter un cifra cuya cantidad de dígitos se conoce con exactitud (cédula de identidad, código de un producto en un almacén, o el carnet de un trabajador) vale el segundo enfoque. Pero si la longitud es variable y desconocida vale el primer enfoque con un búfer tan grande como la máxima longitud admisible.

Ya eso depende del caso que se quiera trabajar ..... Y saludos como siempre  :D


Título: Re: Recibir int seguro
Publicado por: Yoel Alejandro en 9 Marzo 2014, 17:48 pm
Una recomendación a ivance96. Para que el código sea más portable, recordemos que los tipos enteros no tienen una longitud rígidamente definida, por ejemplo en algunas máquinas (antiguas) los enteros son de 16bits, y en otras de 32bits. Si mal no recuerdo, la norma indica que la longitud de int debe estar entre la longitud de short y la de long, pero la elección entre ambos depende de la implementación. Debiendo ser la longitud de short de al menos 16 bits, y la de long de al menos 32 bits. Así que por ejemplo una implementación en particular (quizá en el futuro, para una máquina potente) pudiera tener short de 32 bits, int de 64 bits y long de 64 bits.

La biblioteca <limits.h> define los máximos posibles para los distintos tipos aritméticos según la puesta en práctica. Por ejemplo, CHAR_MAX, SHRT_MAX, UINT_MAX define los máximos de char, short int y unsigned int respectivamente, y son valores que dependen de la implementación de C en tu máquina particular. Así que el programa podría quedar algo así como:
Código
  1. /** INT && UINT **/
  2. if(s[0]=='-')
  3. sign=true;
  4. if ( allNumeric(s.substr(sign, s.size()))) {
  5. if(sign) {
  6.   if ( atoi(s.substr(1, s.size())) <= INT_MAX  )
  7. type+=type_int;
  8. }else{
  9. if ( atoi(s.substr(0, s.size())) <= INT_MAX )
  10. type+=type_int;
  11. if ( atoi(s.substr(0, s.size())) <= UINT_MAX)
  12. type+=type_uint;
  13. }
  14. }

el cual es transportable sin problemas de una máquina a otra.


Título: Re: Recibir int seguro
Publicado por: leosansan en 9 Marzo 2014, 18:15 pm

Lo de un número fijo era por lo de las tarjetas de crédito y cosas así. Como en este código, sencillo pero muy funcional ... y sólo admite un número determinado de caracteres, si son menos  espera hasta que se complete y no da opción de más:

Código
  1. /*Crear un programa que pida al usuario su contraseña (numérica).
  2. Deberá terminar cuando introduzca como contraseña el número
  3. 5879, pero
  4. volvérsela a pedir teniendo cuatro intentos.*/
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7.  
  8. int main( void )
  9. {
  10.    char clave [5];
  11.    int i,clave_num,intentos=0,DIGITOS=4;
  12.    do
  13.    {
  14.        printf ("\n\n\n\Presione una tecla para continuar: ");
  15.        fflush (stdout);
  16.        getch ();
  17.        system ("cls");
  18.        fflush (stdout);
  19.        printf ("\n\n\n   INTRODUZCA LA CLAVE  entera (cuatro digitos) \n\n\n");
  20.        printf ("Teclea la clave (1122 par salir) y presione enter para salir: ");
  21.        fflush (stdout);
  22.        for (i=0;i<DIGITOS;i++){
  23.            clave [i] =getch();
  24.            printf("*");
  25.            fflush (stdout);
  26.        }
  27.    clave [4] ='\0';
  28.    printf ("\n\n\t\t\tLA CLAVE INTRODUCIDA ES :%s",clave );
  29.    fflush (stdout);
  30.    printf ("\n\n\t\t\tLA CLAVE EN ASCII es  : ");
  31.    fflush (stdout);
  32.    for (i=0;i<4;i++){
  33.                printf ("%d - ",clave[i] );
  34.                fflush (stdout);
  35.            }
  36.  
  37.    intentos++;
  38.    clave_num = atoi (clave);
  39.    printf ("%d \n",clave_num);
  40.    if (intentos==3){
  41.            printf ("\n\n\t\tMe quedo con la tarjeta,,AGURRRRRRRRRRR\n\n");
  42.            exit (0) ;
  43.        }
  44.    }
  45.    while (clave_num!=5879);
  46.    printf ("\n\n\n\HASTA OTRA OCASION\n\n\n");
  47.    return 0;
  48. }

Como ves hay más formas de limitar el número de caracteres que los expuestos anteriormente. Este en particular el anterior "me llena", nada de tener que limpiar buffer ni cosas por el estilo.

Y perdón por el uso de getch() pero en el estándar de C no hay ninguna función similar, a no ser que se recurra a getchar() pero con una rápida limpieza de pantalla y aún así se pierden los otros datos impresos.

A mí personalmente el que fgets "meta" un retorno de carro en las cadenas me da un no sé qué .......

Y por cierto, aunque es otro tema, te he echado de menos en el hilo de los rombos. Está bastante animado y creo que aún no se ha acabado. Yo al menos me guardo un as en la manga .... y en C++ para variar.[/size]

¡¡¡¡ Saluditos! ..... !!!!


(http://st.forocoches.com/foro/images/smilies/aaaaa.gif)


Título: Re: Recibir int seguro
Publicado por: ivancea96 en 9 Marzo 2014, 19:50 pm
Una recomendación a ivance96. Para que el código sea más portable, recordemos que los tipos enteros no tienen una longitud rígidamente definida, por ejemplo en algunas máquinas (antiguas) los enteros son de 16bits, y en otras de 32bits. Si mal no recuerdo, la norma indica que la longitud de int debe estar entre la longitud de short y la de long, pero la elección entre ambos depende de la implementación. Debiendo ser la longitud de short de al menos 16 bits, y la de long de al menos 32 bits. Así que por ejemplo una implementación en particular (quizá en el futuro, para una máquina potente) pudiera tener short de 32 bits, int de 64 bits y long de 64 bits.

La biblioteca <limits.h> define los máximos posibles para los distintos tipos aritméticos según la puesta en práctica. Por ejemplo, CHAR_MAX, SHRT_MAX, UINT_MAX define los máximos de char, short int y unsigned int respectivamente, y son valores que dependen de la implementación de C en tu máquina particular. Así que el programa podría quedar algo así como:
Código
  1. /** INT && UINT **/
  2. if(s[0]=='-')
  3. sign=true;
  4. if ( allNumeric(s.substr(sign, s.size()))) {
  5. if(sign) {
  6.   if ( atoi(s.substr(1, s.size())) <= INT_MAX  )
  7. type+=type_int;
  8. }else{
  9. if ( atoi(s.substr(0, s.size())) <= INT_MAX )
  10. type+=type_int;
  11. if ( atoi(s.substr(0, s.size())) <= UINT_MAX)
  12. type+=type_uint;
  13. }
  14. }

el cual es transportable sin problemas de una máquina a otra.
Si, bueno. Salvo en el float y el double, que era un terrible follón traducirlo de char* a float/double, en las otras comprobaciones lo intentaba hacer sin las funciones atoi y parecidas.


Título: Re: Recibir int seguro
Publicado por: patilanz en 10 Marzo 2014, 21:55 pm
Hola con la ayuda de sus codigos he hecho esto:

Código
  1. #include "stdafx.h"
  2. #include <stdlib.h>
  3. #include <cctype>
  4. #include "firstTest.hpp"
  5.  
  6.  
  7. int getInt(char *msg,int max,int min){
  8. char *str=new char[max+1];
  9. do{
  10. fflush(stdout);
  11. printf("%s",msg);
  12. fgets(str,max+1,stdin);
  13. int i=-1;
  14. bool clean=true;
  15. while(str[++i]!='\0'){
  16. if(str[i]=='\n'){
  17. clean=false;
  18. str[i]='\0';
  19. }
  20. }
  21. if(clean)getchar();
  22. str=outFirstSpace(str);
  23. printf("\n");
  24.  
  25.  
  26. }while(*str=='\0' || !isInt(str) || getLength(str)<min);
  27. //while(getchar()!='\n');
  28. return atoi(str);
  29. }
  30.  
  31. char * outFirstSpace(char *str){
  32. int length=getLength(str);
  33. while(*str==' ')str++;
  34. return str;
  35. }
  36.  
  37. bool isInt(char const * const str){
  38. int length=getLength(str);
  39. for(int i=0;i<length;i++){
  40. if(!isdigit(str[i])){
  41. return false;
  42. }
  43. }
  44. return true;
  45. }
  46.  
  47. int getLength(const char * str){
  48. int l=0;
  49. while(*(str++)!='\0')l++;
  50. return l;
  51. }
  52.  

Hay un problema que si se insertan mas caracteres de los que pide fgets() luego repite el printf("%s",msg) muchas veces. No se que pasa pero no he conseguido limpiar el buffer. Sabéis como?


@Edit: Acabo de crear otro con iostream en c++

Código
  1. #include "stdafx.h"
  2. #include "secondTest.hpp"
  3.  
  4.  
  5. int getInt(string msg,int max,int min){
  6. string str;
  7. do{
  8. cout << msg;
  9. cin.clear();
  10. cin >> str;
  11. }while(!isInt(str) || str.size()>max || str.size()<min);
  12. return atoi(str.data());
  13. }
  14.  
  15. bool isInt(string str){
  16. for(size_t i=0;i<str.size();i++){
  17. if(!isdigit(str[i]))
  18. return false;
  19. }
  20. return true;
  21. }

Este no tiene problemas o por lo menos no los he descubierto.

Saludos


Título: Re: Recibir int seguro
Publicado por: Yoel Alejandro en 11 Marzo 2014, 03:29 am
Leo, tu programa es ideal para el caso de una clave numérica con una cantidad exacta de dígitos, además obliga al usuario sobre meter exactamente esa cantidad.

Y respecto a los rombos, ....... tranquilo jaja que ya voy a aparecer con una idea que he estado pensando y espero sorprendente  ;D (tanto como las de todos)


Título: Re: Recibir int seguro
Publicado por: rir3760 en 11 Marzo 2014, 03:37 am
El método que propones tiene un pero, si introduces 12as toma 12 como int.
En ese caso nada impide verificar el resto del stream con una lectura adicional, las funciones eof/get, etc. Por ejemplo:
Código
  1. #include <iostream>
  2. using std::cin;
  3. using std::cout;
  4. using std::endl;
  5.  
  6. #include <string>
  7. using std::string;
  8. using std::getline;
  9.  
  10. #include <sstream>
  11. using std::istringstream;
  12.  
  13. int main()
  14. {
  15.   bool ok = false;
  16.   int num;
  17.  
  18.   do {
  19.      cout << "Introduce un numero: ";
  20.      string linea;
  21.      getline(cin, linea);
  22.  
  23.      istringstream is(linea);
  24.      char ch;
  25.      if (is >> num && !(is >> ch))
  26.         ok = true;
  27.      else
  28.         cout << "Entrada no valida" << endl;
  29.   }while (!ok);
  30.  
  31.   cout << "num == " << num << endl;
  32.  
  33.   return 0;
  34. }

----

La biblioteca <limits.h> define los máximos posibles para los distintos tipos aritméticos según la puesta en práctica. Por ejemplo, CHAR_MAX, SHRT_MAX, UINT_MAX define los máximos de char, short int y unsigned int respectivamente, y son valores que dependen de la implementación de C en tu máquina particular. Así que el programa podría quedar algo así como:
Código
  1. /** INT && UINT **/
  2. if(s[0]=='-')
  3. sign=true;
  4. if ( allNumeric(s.substr(sign, s.size()))) {
  5. if(sign) {
  6.   if ( atoi(s.substr(1, s.size())) <= INT_MAX  )
  7. type+=type_int;
  8. }else{
  9. if ( atoi(s.substr(0, s.size())) <= INT_MAX )
  10. type+=type_int;
  11. if ( atoi(s.substr(0, s.size())) <= UINT_MAX)
  12. type+=type_uint;
  13. }
  14. }
Un problema con ese fragmento es el uso de atoi, por razones obvias su valor de retorno no puede ser mayor a INT_MAX. Para conocer si un numero esta fuera del rango valido para un tipo entero se deben utilizar las funciones strtol, strtoll (C++11), etc. dependiendo del caso (y del modo de compilación).

Un saludo