Usar fgets y scanf en conjunto en C

C – Limpiar búfer al usar scanf y fgets

Hay un pequeño problema que ocurre en C al usar scanf en conjunto con fgets. Es decir, primero usar scanf para leer un valor, e inmediatamente después usar fgets.

El problema es que al usar scanf en primer lugar, el programa no “espera” a que el usuario introduzca la cadena con fgets. Como resultado, se lee una cadena vacía o “nada”.

Este error también se me ha presentado al usar scanf en algunos ciclos y en conjunto con otras funciones que leen de stdin.

Por eso hoy te mostraré una solución para este problema.

Demostración del problema

Comencemos viendo el problema al usar scanf y después fgets, pues pareciera que fgets no funciona:

/*
    https://parzibyte.me/blog
*/
#include <stdio.h>
#include <string.h> // Para strlen

#define MAXIMA_LONGITUD_CADENA 50

int main(int argc, char const *argv[])
{
    int edad;
    printf("Escribe tu edad: ");
    scanf("%d", &edad);
    char nombre[MAXIMA_LONGITUD_CADENA];
    printf("Ingresa tu nombre completo: ");
    fgets(nombre, MAXIMA_LONGITUD_CADENA, stdin);
    // Remover salto de línea
    if ((strlen(nombre) > 0) && (nombre[strlen(nombre) - 1] == '\n'))
    {
        nombre[strlen(nombre) - 1] = '\0';
    }
    printf("Tu nombre es %s", nombre);
    return 0;
}

Al ejecutarlo y escribir la edad, el programa no espera a que el usuario introduzca el nombre y sea leído con fgets:

Error de scanf con fgets en C
Error de scanf con fgets en C

Esto sucede porque scanf solo lee el número pero no consume el salto de línea que dejamos, cosa que sí hace fgets, leyendo la línea vacía y sin “esperar” a que el usuario ingrese el valor.

Solución: limpiar búfer

Para evitar este problema debemos limpiar el búfer o consumir la nueva línea justo después de usar printf. Hay varias maneras de hacer esto, la que me ha funcionado es:

void consumirNuevaLinea(void)
{
    int c;
    do
    {
        c = getchar();
    } while (c != EOF && c != '\n');
}

En este caso getchar simplemente va a consumir el siguiente carácter, aunque no nos importa cuál carácter sea ese. Se va a detener cuando encuentre el fin del archivo o el salto de línea.

Puedes invocar a esta función desde cualquier lugar. No requiere argumentos ni devuelve nada porque lee de la entrada estándar.

Poniendo todo junto

El código junto con la solución y ejemplo de uso queda así:

/*
    https://parzibyte.me/blog
*/
#include <stdio.h>
#include <string.h> // Para strlen

#define MAXIMA_LONGITUD_CADENA 50

void consumirNuevaLinea(void)
{
    int c;
    do
    {
        c = getchar();
    } while (c != EOF && c != '\n');
}

int main(int argc, char const *argv[])
{
    int edad;
    printf("Escribe tu edad: ");
    scanf("%d", &edad);
    // Consumir nueva línea
    consumirNuevaLinea();

    char nombre[MAXIMA_LONGITUD_CADENA];
    printf("Ingresa tu nombre completo: ");
    fgets(nombre, MAXIMA_LONGITUD_CADENA, stdin);
    // Remover salto de línea
    if ((strlen(nombre) > 0) && (nombre[strlen(nombre) - 1] == '\n'))
    {
        nombre[strlen(nombre) - 1] = '\0';
    }
    printf("Tu nombre es %s", nombre);
    return 0;
}

Al ejecutarlo, funciona como un encanto:

Usar fgets y scanf en conjunto en C
Usar fgets y scanf en conjunto en C

Conclusión

Esta es una de las cosas que más me molestan, intrigan o como se diga, de C. Si quieres leer una cadena con espacios no puedes usar scanf, debes usar fgets, en donde además debes remover el salto de línea.

Y si usas scanf con fgets debes consumir la nueva línea. También debes tener cuidado de no causar un desbordamiento de búfer.

Cosas que con Python, Go o incluso Java no pasan. Pero bueno, C es un lenguaje amigable al final de todo, solo que sí, elige muy bien a sus amigos.

Puede que haya soluciones más elegantes o simples, pero eso no quita el problema principal.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *