funcion limpiar buffer de teclado

Iniciado por Locura_23, 6 Octubre 2021, 16:51 PM

0 Miembros y 2 Visitantes están viendo este tema.

Locura_23

Buenas a todos,

Tengo una duda con la siguiente función... la saqué de un libro de texto. Y es una solución para limpiar buffer de entrada de teclado (sin usar el vaciado de fflush() ) en C, antes de leer un string. Solo que cuando la utilizo en la consola, tengo que oprimir dos veces  salto de linea, una vez cuando ingreso el dato y otra vez mas para que salte de linea.  A diferencia de ingresar el dato y oprimir salto de linea "enter", y que se guarde el dato de inmediato.

Alguien sabe una alternativa a esta función ?

Saludos



void limpiarBuffer()
{
   char c;

   do
   {
       c = getchar();
   }while( c != '\n' );

}


RayR

Las formas estándar y portables de hacerlo son sólo variaciones de la que has puesto. En C++ puedes usar cin.ignore:

Código (cpp) [Seleccionar]
cin.ignore(tam, '\n');

que lee y descarta hasta "tam" caracteres o hasta encontrar un '\n'. Puedes usar un número muy grande para tam, o bien, numeric_limits<streamsize>::max(), que es el tamaño máximo de un stream:

Código (cpp) [Seleccionar]
#include <limits>
...
cin.ignore(numeric_limits<streamsize>::max(), '\n');


que básicamente significa que limpie todo lo que haya, hasta que encuentre el caracter de línea nueva. Hay alguna otra manera pero a final de cuentas, como te dije, son variaciones de esto.

Eso sí, evita fflush(stdin), que es directamente erróneo aunque a veces funcione. Otra solución que a veces se lee es mediante fseek, pera tampoco deberías usarla. Entre otras cosas no es portable y no hay ni siquiera garantía de que siga funcionando en las plataformas donde actualmente lo hace.

Lo del doble Enter no debería ser necesario. Creo que sé dónde está tu error, pero sería mejor su pusieras un código de ejemplo donde pase.

MAFUS

Bueno, ahí va un hack que me ha funcionado siempre:

fseek(stdin, 0, SEEK_END);

Locura_23

#3
Cita de: MAFUS en  6 Octubre 2021, 18:19 PM
Bueno, ahí va un hack que me ha funcionado siempre:

fseek(stdin, 0, SEEK_END);

Hola MAFUS, funciona bien ese hack xD gracias!

------------------------------------------------------

RayR gracias por tu aporte !

El tema es que solo uso C en este programa, tengo entendido que no es buena práctica combinar las cosas no?

Por alguna razón, en el momento de publicar la pregunta eso me pasaba (del doble enter) y me ha pasado en otros programas donde incluí esta función que mencioné. Aveces pasa y aveces no, es raro... acá dejo donde la estaba usando en este caso.



#include <stdio.h>
#include <stdlib.h>
#include <sysinfoapi.h>

typedef struct
{
   char nombreDragon[30];
   int numeroClase;
   char color[15];
   int puntaje;
   int mes;
   int dia;
}dragon;

void limpiarBuffer()
{
   char c;

   do
   {
       c = getchar();
   }while( c != '\n' );

} // fin funcion

dragon cargarDatosDragon()
{
   dragon aux;
   SYSTEMTIME datosTiempo;

   printf("\n<< Carga de datos >>\n");

   printf("\nIngrese el nombre del dragon >> ");
   limpiarBuffer();
   gets(aux.nombreDragon);

   printf("\nIngrese el numero de clase >> ");
   scanf("%d",&aux.numeroClase);

   printf("\nIngrese el color >> ");
   limpiarBuffer();
   gets(aux.color);

   printf("\nIngrese el puntaje >> ");
   scanf("%d",&aux.puntaje);

   GetLocalTime(&datosTiempo);

   aux.mes = datosTiempo.wMonth;
   aux.dia = datosTiempo.wDay;

   return aux;

} // fin funcion

int main()
{
   dragon nuevoDragon;
   
   nuevoDragon = cargarDatosDragon();

   return 0;

} // fin main



pd: ignorese los gets() y demas cosas mejorables, es solo un boceto jaja


MAFUS

El problema con esa función es que si el buffer ya está vacío getchar tomará el control de la consola obligando a que el buffer se llene otra vez. Es decir: esa función sólo hace el trabajo esperado si en el buffer quedó algo.

Locura_23

Cita de: MAFUS en  6 Octubre 2021, 20:51 PM
El problema con esa función es que si el buffer ya está vacío getchar tomará el control de la consola obligando a que el buffer se llene otra vez. Es decir: esa función sólo hace el trabajo esperado si en el buffer quedó algo.

mm ya veo, tiene total sentido eso que decis. De ahi que aveces me haga eso y otras no

RayR

La clave es limpiar el buffer sólo cuando sabes que no está vacío. ¿Cómo puedes saber esto? Depende, pero de forma muy general, cuando lees, por ejemplo, enteros y flotantes, scanf siempre deja el '\n' en el buffer, por lo que necesitas limpiarlo si la siguiente instrucción de entrada lee caracteres o cadenas. La función gets quita el '\n', así que no tienes que limpiarlo después de llamarla. Si lo haces, se producirá  el mismo problema de tener que introducir dos veces el salto de línea. Si usas fgets, la cosa cambia, pero eso lo puedes consultar en la documentación de esa función en cualquier manual.

P.D. La razón por la que te mencioné que no te aconsejaba la solución con fseek es que, por ejemplo, no funciona en Linux. Esto no es falla del SO, sino que en realidad los "streams", cuando no están asociados a un archivo real, no tienen por qué admitir búsquedas. La entrada estándar del teclado no es un archivo normal, así que puede o no admitirlas. En Windows funciona (por ahora) pero no es seguro saber si siempre lo hará. Esto no es meramente teórico; la propia fflush(stdin) funcionaba antes en Visual C++, y de hecho, la documentación oficial de Microsoft así lo especificaba, y sin embargo, hace unos años eliminaron esa funcionalidad y ya no funciona ni se menciona en la documentación actual. Así que la solución del fseek perfectamente podría dejar de funcionar en la siguiente actualización (o no, quién sabe) sin previo aviso. De cualquier manera, puesto que la manera estándar (como tu función) es tan simple, portable, y su funcionamiento está garantizado, no le veo sentido a usar una que no cumple con estas ventajas. En todo caso, si de todas maneras la quieres usar, deberías considerarla como a gets (algo temporal que igual vale para ejercicios de práctica, pero nada más). Y recomiendo checar esto: https://blog.codinghorror.com/the-works-on-my-machine-certification-program/

Algunas referencias sobre las búsquedas en stdin, por si te interesa.Para mi gusto las explicaciones están algo incompletas, y hay aún más razones para evitarlo, pero no están mal (en particular, la respuesta aceptada del primer linky sus comentarios):

https://stackoverflow.com/questions/16672672/can-fseekstdin-1-seek-set-or-rewindstdin-be-used-to-flush-the-input-buffer-i
https://stackoverflow.com/questions/4917801/using-fseek-with-a-file-pointer-that-points-to-stdin

Locura_23

#7
Siento revivir el tema, pero si alguna vez alguien necesita saber sobre esto, acá dejo otro hack que encontré recién, sirve para leer con la función scanf(), manteniendo limpio el buffer.


/**Muestra como leer un dato con scanf, de manera que el buffer quede limpio despues de la
lectura, no es necesario utilizar el fflush**/

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int num = 0;
   char palabra[15] = {""};

   printf("Ingrese un numero.\n");
   scanf("%i%*c",&num); ///hack

   printf("El numero ingresado fue: %i\n",num);

   printf("\n\nIngrese una palabra.\n");
   gets(palabra);

   printf("\nLa palabra ingresada fue: %s\n",palabra);

   return 0;
}


prueba de que funciona es que tras ingresar el numero entero se puede leer tranquilamente un string.

RayR

Pero eso no deja limpio el buffer. Simplemente lee un caracter, justo como si pusieras un "%c" (o un único getchar() después del scanf). El * hace que no lo asigne a una variable, pero por lo demás, no hay mayor diferencia. Sí te sirve suponiendo que siempre introduzcas puros dígitos, pero si, por error (o no) introduces algo como "15 ", o sea, el número con un espacio (o cualquier otro caracter) al final, vas a volver a tener el mismo problema, lo cual no pasa cuando limpias el buffer, por ejemplo con la función que pusiste en el mensaje original.

Hay formas más complejas de scanf que sí pueden limpiarlo (puedes googlear el especificador '[' ), pero tienen sus inconvenientes, por lo que, en mi opinión no valen la pena para este propósito.