martes, 2 de abril de 2013

Limpiar la entrada estándar en C en Linux, fflush(stdin)

Uno de los problemas que surgen a la hora de programar en C en Linux es que no podemos limpiar los caracteres no deseados que quedan almacenados en ocasiones en la entrada estándar, ya que al ejecutar la función fflush(stdin) no tiene efecto al usarse sobre el flujo de entrada.

Podemos solucionar este problema usando una función como la siguiente:


void fflushin()
{

 int d, i=0;

 d = stdin->_IO_read_end - stdin->_IO_read_ptr;

 for(i=0; i<d; i++)
  getchar();
}


La operación stdin->_IO_read_end - stdin->_IO_read_ptr nos permite calcular el número de caracteres que quedan almacenados en la entrada estándar a partir de la posición actual del puntero (osea, los caracteres sobrantes), después solo tenemos que ejecutar la función getchar() tantas veces como caracteres haya, para limpiar así la entrada estándar y deshacernos de esos caracteres no deseados.


EJEMPLO DE USO

Una situación muy habitual en la que es necesario usar una función como la anterior es la siguiente:

#include <stdio.h>

main()
{

 int num;

 printf("\nInserta un numero: ");
 
 scanf("%d", &num);

 printf("numero insertado: %d", num);

 printf("\n\nPulsa enter para continuar\n\n");

 getchar();
}


En el programa anterior tenemos la intención de usar la función getchar() para pausar la ejecución del programa hasta que el usuario pulse la tecla enter, pero debido a la función scanf se queda un caracter sin leer en la entrada estándar (el caracter de nueva linea), caracter que leerá la función getchar() sin detenerse como teníamos previsto.

Para solucionarlo lo único que tenemos que hacer es limpiar los caracteres no deseados que se han quedado en la entrada estándar usando la función fflushin() que hemos definido:



#include <stdio.h>

void fflushin()
{

 int d, i=0;

 d = stdin->_IO_read_end - stdin->_IO_read_ptr;

 for(i=0; i<d; i++)
  getchar();
}

main()
{

 int num;

 printf("\nInserta un numero: ");
 
 scanf("%d", &num);

 printf("numero insertado: %d", num);

 printf("\n\nPulsa enter para continuar\n\n");

 fflushin();

 getchar();
}




Y de esta manera el programa funciona tal y como habíamos planeado ya que la función fflushin() ha limpiado esos caracteres no deseados de la entrada estándar y por lo tanto la función getchar() detiene la ejecución del programa hasta que el usuario pulse la tecla enter.

Saludos =)


3 comentarios:

  1. Muchas gracias viene estupendo, justamente estaba teniendo problemas en un programilla y sabia que era el buffer. Un saludo

    ResponderEliminar
  2. Hola, a mi me da estos errores, como los puedo arreglar?
    error: ‘FILE’ has no member named ‘_IO_read_end’
    error: ‘FILE’ has no member named ‘_IO_read_ptr’

    ResponderEliminar
  3. Hola Jorge.

    Que distribución de linux estás utilizando??

    Busca en el archivo "/usr/include/libio.h" la estructura "struct _IO_FILE"

    Debería de tener un contenido similar a este:


    [code]
    struct _IO_FILE {
    int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
    #define _IO_file_flags _flags

    /* The following pointers correspond to the C++ streambuf protocol. */
    /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
    char* _IO_read_ptr; /* Current read pointer */
    char* _IO_read_end; /* End of get area. */
    char* _IO_read_base; /* Start of putback+get area. */
    char* _IO_write_base; /* Start of put area. */
    char* _IO_write_ptr; /* Current put pointer. */
    char* _IO_write_end; /* End of put area. */
    char* _IO_buf_base; /* Start of reserve area. */
    char* _IO_buf_end; /* End of reserve area. */
    [/code]

    Como puedes ver, en mi caso, ahí se encuentra tanto el "_IO_read_ptr" como el "_IO_read_end".

    Quizá tu distribución utilice otros nombres para esos elementos de la estructura IO_FILE.

    Si tal envíame el contenido que tiene tu distribucion en la estructura IO_FILE para poder ver que es lo que falla exactamente.

    ResponderEliminar