1

Vengo teniendo un problema con la validación de datos de entrada en una calculadora que estoy haciendo. Si bien desarrollé la función para que sólo permita el ingreso de números, sólo me permite ingresar enteros. Estuve buscando por todo internet cómo permitir también el ingreso de números flotantes y no he podido encontrar una solución. El código es bastante largo así que les compartiré sólo el principio hasta la sección de ingreso de datos y también el de la función que lo tengo en una biblioteca a ver si pueden darme una manito. Saludos!

#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>
#include "CalculosAritmeticos.h"

int main()
{
    char primerIngreso[128];
    char segundoIngreso[128];
    float primerNumero;
    float segundoNumero;
    float suma;
    float resta;
    float division;
    float producto;
    int opcion;
    char salir;
    int aux1;
    int aux2;
    primerNumero = 0;
    segundoNumero = 0;
    salir = 'n';

    do{
        printf("\n1- Ingresar 1er operando (A = %.2f)\n",primerNumero);
        printf("2- Ingresar 2do operando (B = %.2f)\n",segundoNumero);
        printf("3- Calcular la suma (A+B)\n");
        printf("4- Calcular la resta (A-B)\n");
        printf("5- Calcular la division (A/B)\n");
        printf("6- Calcular la multiplicacion (A*B)\n");
        printf("7- Calcular el factorial (A!)\n");
        printf("8- Calcular todas las operaciones\n");
        printf("9- Salir\n\n");
        printf("Opcion: ");
        scanf("%d",&opcion);

        switch(opcion)
        {
            case 1:
                do
                {
                    fflush(stdin);
                    printf("\nIngrese el 1er operando: ");
                    scanf("%s",primerIngreso);
                    aux1 = ValidarNumero(primerIngreso);
                }while(aux1 == 0);
                primerNumero = atof(primerIngreso);
                break;
            case 2:
                do
                {
                    fflush(stdin);
                    printf("\nIngrese el 2do operando: ");
                    scanf("%s",segundoIngreso);
                    aux2 = ValidarNumero(segundoIngreso);
                }while(aux2 == 0);
                segundoNumero = atof(segundoIngreso);
                break; 


**Y este es el código de la función**


#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>
#include "CalculosAritmeticos.h"

/** \brief Recibe los datos ingresado y los valida para reconocer si es de tipo numérico o caracter
 * \param For realiza una iteración donde compara el índice de repeticiones con la longitud de la cadena
          e imprime un mensaje de error en caso que no sea un valor entero o de punto flotante y devuelve 0 como valor
 * \return Retorna 1 en caso que no ingrese en la iteración, validando que el dato ingresado es de tipo numérico
 */
float ValidarNumero(char caracter[])
{
    int i;
    for(i=0; i<strlen(caracter); i++)
    {
        if(!(isdigit(caracter[i])))
        {
            printf("\nError. Ingrese solo numeros: \n");
            return 0;
        }
        if (i==strlen(caracter))
        {
            return 1;
        }
    }
    return 1;
} ```
Concord
  • 13
  • 3
  • 1
    Varias cosas: 1) Sólo verificas que sean dígitos, cuando claramente necesitas el punto decimal también. ¿O quieres usar una coma? 2) `if (i==strlen(caracter))` es innecessario, ya que jamás será true. 3) Por qué `ValidarNumero` devuelve float, cuando sólo devuelve 0/1 o true/false? Es confuso. 4) Alrededor de `isdigit` hay paréntesis de más. Así que volviendo a tu problema principal, creo que si `ValidarNumero` no acepta un punto decimal, es bastante lógico que tu programa sólo te permita enteros. :P Deberás validar además que no haya más de uno. – Andrew Sep 03 '20 at 04:58

2 Answers2

1

Puedes usar directamente la función scanf para pedir el dato flotante y aprovechar el valor que retorna dicha función, ya que scanf retornará la cantidad de elementos que pueda leer correctamente e incluso podría ser 0 si no se llega a leer ningún elemento.

Lo que debes hacer es definir una función denominada validarDato y lograr que dicha función sirva para validar datos de tipo int, float, etc.

Además, la función retornará 1 si el usuario no cumple con el formato especificado, de lo contrario, devuelve 0.

La implementación quedaría así:

int validarDato(const char* fm, void* var)
{
    int ch;
    /* Sí scanf devuelve 0, es porque no se cumplió con el formato especificado. */
    if(!scanf(fm, var))
    {
        /* Limpiamos el búfer del teclado, así logramos que la próxima llamada de scanf pause el programa. */
        while((ch = getchar()) != '\n' && ch != EOF);
        return 1;
    }

    /* Verifica sí el búfer quedó sucio. */
    if(getchar() != '\n')
    {
        /* Limpiamos el búfer del teclado. */
        while((ch = getchar()) != '\n' && ch != EOF);
        return 1;
    }
    return 0;
}

Esta función no permitirá ingresar caracteres extraños, es más, si el usuario introduce el dato de esta forma:

243.fd
fdd
.sds
23..23

La función lo tomará como incorrecto.

Ejemplo de uso:

int main(void)
{
    float a;
    do
    {
        printf("Ingrese un float: ");
        
    }while(validarDato("%f", &a));
    return 0;
}

Nota: La función también sirve para validar datos enteros, ejemplo:

int main(void)
{
    int a;
    do
    {
        printf("Ingrese un int: ");
        
    }while(validarDato("%d", &a));
    return 0;
}

Fuente: https://es.stackoverflow.com/a/375841/105299

MrDave1999
  • 7,491
  • 1
  • 7
  • 22
  • Solo para corregir, la funcion `scanf` retorna la cantidad de argumentos leidos. No "retornará 1 si el usuario no cumple con el formato especificado", si retorna 1 leyo exitosamente lo que se le pidio – Pablochaches Sep 03 '20 at 19:25
  • @Pablochaches ya corregí esa parte, gracias – MrDave1999 Sep 03 '20 at 19:31
  • En la parte de " Verifica sí el búfer quedó sucio", ¿no sucederá que si no quedó sucio, el usuario tendrá que apretar un `` de más por ese `getchar`? Yo cuando usaba C utilizaba `kbhit` para ver si había alguna tecla presionada pendiente. – Andrew Sep 03 '20 at 19:55
  • @Andrew cuando la función `scanf` lee datos enteros o flotantes, el búfer como mínimo quedará con un salto de línea.. – MrDave1999 Sep 03 '20 at 20:06
  • @MrDave1999 Mil gracias tu código me ayudó muchísimo! Una última pregunta, si quisiera limpiar el buffer al inicio donde muestra en el menu el valor por defecto (que es 0), en vez de inicializarlo como hice ya que es una mala práctica asignarle un valor a una variable para luego pedirle que ingrese uno, en qué parte de la función de validacion tendría que poner el fflush? Ya que el scanf se encuentra ahi – Concord Sep 03 '20 at 20:14
  • @Concord No deberías de usar la función `fflush` para vaciar el búfer `stdin`, ya que esa función solo limpia el búfer `stdout`. En tu caso no hace falta el `fflush`, porque ya la función `validarDato` hace todo, lee el dato y deja el búfer del teclado limpio. – MrDave1999 Sep 03 '20 at 20:24
0

Aquí te dejo cómo podrías mejorar un poco la función de validación:

int ValidarNumero(char caracteres[])
{
    int i;
    int tienePunto = 0;
    for (i = 0; i < strlen(caracteres); i++)
    {
        if (caracteres[i] == '.')
            tienePunto++;

        if ((!isdigit(caracteres[i]) && caracteres[i] != '.') || tienePunto > 1)
        {
            printf("\nError. Ingrese solo numeros:\n");
            return 0;
        }
    }
    return 1;
}

Si tu compilador no permite declaración e inicialización juntas, deberás separar la línea de tienePunto.

También podrías echarle un vistazo a esta otra pregunta (en inglés).

Andrew
  • 346
  • 1
  • 10
  • Lo modifiqué como sugeriste pero sigue permitiendo sólo el ingreso de enteros y me rechaza los flotantes y no sabría por qué – Concord Sep 03 '20 at 19:10
  • Es tan fácil como correrlo paso a paso y ver por qué sucede. Claramente tiene que haber algo que no sea dígito o punto. – Andrew Sep 03 '20 at 19:53
  • entiendo lo que va haciendo paso a paso, pero sigue sin permitir ingresar flotantes, cosa curiosa – Concord Sep 03 '20 at 20:09
  • El problema del código es que solo cuenta los puntos, pero no verifica si el caracter es un punto, por lo tanto, `isDigit` no lo tomará como un dígito y hace que la función retorne `0`.. – MrDave1999 Sep 03 '20 at 20:43
  • @MrDave1999, es verdad, ahí lo corregí. – Andrew Sep 03 '20 at 22:49