¿Queda algo en el Buffer de Entrada stdin? (Solucionado)

Iniciado por AlbertoBSD, 24 Noviembre 2018, 18:54 PM

0 Miembros y 1 Visitante están viendo este tema.

AlbertoBSD

Solución

Se encontraron 2 soluciones

  • Detectar en la memoria previamente leida por fgets si el buffer de destino tiene el retorno de linea, si esi ya no hay nada en STDIN, en caso contrario aun quedan datos.
  • realizar fseek al stdin para que avance hasta el final del archivo. (Solucion gracias a MAFUS)


    #include<stdio.h>
    #include<string.h>

    int main() {
    int len,i = 0;
    char temporal[10];
    char c;
    printf("Ingrese una cadena: ");
    fgets(temporal,10,stdin);
    printf("Cadena Leida: %s\n",temporal);
    len = strlen(temporal);
    if(temporal[len - 1 ]  != 0xA) {
    while ((c = getchar()) != 0xA);
    }
    printf("Ingrese otra cadena: ");
    fgets(temporal,10,stdin);
    printf("Cadena Leida: %s\n",temporal);
    }



    con fseek:


    #include <stdio.h>
         
        int main() {
            char c;
            while(1) {
                printf("> ");
                c = getchar();
                fseek(stdin, 0, SEEK_END);
                printf("Caracter: %c\n", c);
            }
        }




    Post Original

    Muy buen dia aun que la pregunta en el titulo de este post parece una pregunta totalmente N00b. No lo es.

    El objetivo del post es Dejar de utilizar fflush

    fflush(stdin);

    Yo no lo utilizo, o por lo menos trato de nunca recomendarlo, solo que ayer se lo recomendé a alguien y no me convence su implementación.  :silbar:

    La verdad es que quiero dar por terminado el tema de la función fflush para el Buffer de entrada.

    Todo esto en Lenguaje C  ;-)

    La idea de este post viene de preguntas hechas en el foro similares a ¿Como filtrar todo tipo de datos de entrada en un programa?

    Revisando algunas links en internet me encuentro con:
    https://es.stackoverflow.com/questions/82431/saber-si-el-b%C3%BAfer-de-entrada-stdin-est%C3%A1-vac%C3%ADo-en-c-est%C3%A1ndar
    http://man7.org/linux/man-pages/man3/fflush.3.html
    http://www.cplusplus.com/reference/cstdio/fflush/

    Sin embargo en todos ellos hablan de que el comportamiento de fflush para el stdin es Inesperado, por lo cual repetidamente dicen que no se debe de usar.

    ¿Cual es la solución para limpiar el buffer de entrada, si es que realmente queda algo en el?

    Intente con feof, sin embargo al parecer NUNCA llega el fin del archivo para STDIN

    #include<stdio.h>
    #include<string.h>

    int main() {
    char temporal[10];
    printf("Ingrese menos de 10 o mas de 10 caracteres\npara ver el comportamiento de feof(stdin): ");
    fgets(temporal,10,stdin);
    temporal[strcspn(temporal,"\n\r")] = '\0';
    printf("Valor leido %s\n",temporal);
    if(feof(stdin)) {
    printf("stdin llego al final del archivo\n");
    }
    else {
    printf("stdin NO llego al final del archivo\n");
    }
    }


    El problema de todo esto radica que cuando estamos leyendo multiples valores desde el teclado, aun que lo delimitemos con fgets para la cantidad de datos que se guardaran en cada variable, el fgets , al igual que otras funciones de entrada deja los caracteres restantes en el buffer de entrada y son tomados automaticamnete por cualquier funcion de entrada de texto.




    Como ven en la imagen, en la segunda entrada de las AAAAAAAAAAAAA, se salto el segundo "Ingrese una cadena."

    #include<stdio.h>
    #include<string.h>

    int main() {
    char temporal_1[10];
    char temporal_2[10];
    printf("Ingrese una cadena: ");
    fgets(temporal_1,10,stdin);
    temporal_1[strcspn(temporal_1,"\n\r")] = '\0';
    printf("Ingrese una cadena: ");
    fgets(temporal_2,10,stdin);
    temporal_2[strcspn(temporal_2,"\n\r")] = '\0';
    printf("Valor leido %s\n",temporal_1);
    printf("Valor leido %s\n",temporal_2);
    }


    Este problema tambien se presenta en C++ con funciones de entrada tipo cin>>

    Ejemplo:



    Codigo:

    Código (cpp) [Seleccionar]
    #include<iostream>

    using namespace std;
    int main() {
    int numero_1,numero_2;
    cout<<"Ingrese un numero: ";
    cin>>numero_1;
    cout<<"Ingrese un numero: ";
    cin>>numero_2;
    cout<<"Valor leido "<<numero_1<<endl;
    cout<<"Valor leido "<<numero_2<<endl;
    }


    Entonces pregunto:

    ¿Cual es la solución para limpiar el buffer de entrada, si es que realmente queda algo en el?
    ¿Como se si es que realmente queda algo en el buffer de entrada, sin consumirlo.?



    https://albertobsd.blogspot.com/2018/11/queda-algo-en-el-buffer-de-entrada-stdin.html
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

K-YreX

Yo la verdad también tengo esa duda ya que por mucho que he leído y he buscado información no he encontrado respuesta a tu pregunta. Sin embargo aunque no suelo programar mucho en C no suelo tener el problema del buffer pero por ejemplo intentando ayudar en un post que dejo a continuación:
https://foro.elhacker.net/programacion_cc/necesito_ayuda_en_este_rpograma_quiero_que_pregunte_si_quiero_hacer_otro_correo-t489865.0.html
me pasaba lo mismo porque quedaban los saltos de línea en el buffer y entonces se me saltaban algunas de las entradas de datos. El tema de los saltos de línea pude solucionarlos como buenamente pude (puedes ver el código que dejé en ese post para ver las soluciones que encontré aunque son unas soluciones un poco caseras para salir del paso) ya que no quería usar <fflush> ya que también he leído que no debe usarse para el buffer de entrada, sino para el de salida.

Sin embargo me siguió quedando un problema en ese código que dejé comentado también para ver si alguien podía ayudarme a solucionarlo pero al final se ha quedado ahí y yo me he quedado sin una respuesta. :-\
Espero que alguien nos pueda ayudar a resolver esta duda tan básica pero que todo el mundo pasa por alto... :-X
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

AlbertoBSD

#2
Tengo que decirles que después de meditarlo un poco, encontre la solución, se puede deducir leyendo la memoria que acabamos de leer, esto en el caso de Usar Fgets  ;-) ;-)

Si bien han usado en su momento fgets, recordaran que este guarda en la misma cadena que acaba de leer el "Enter" esto es un byte de valor 0xA (10 en decimal)

Esto siempre y cuando la longitud del texto leído no supere el tamaño del arreglo donde vamos a guardar nuestra cadena leida.

Vemos un ejemplo con Codigo:


#include<stdio.h>
#include<string.h>

int main() {
int len,i = 0;
char temporal[10];
printf("Ingrese una cadena: ");
fgets(temporal,10,stdin);
len = strlen(temporal);
printf("Cadena Leida: %s",temporal);
printf("La longitud de la cadena leida es de %i\n",len);
while(i<10) printf("%.2X",temporal[i++]);
printf("\n");
}



Aqui unas cuantas salidas.



1er caso de la imagen: ingresar mas de 9 letras "A", obvio hay datos en el buffer de stdin esperando ser leeidos,
2do caso de la imagen: Al ingresar exactamente 9 letras A, el ultimo byte del buffer el byte 10 es el valor nulo '\0' En este caso queda un Enter en el buffer de datos de stdin
3er caso de la imagen: Al ingresar exactamente 8 letras A, strlen sigue considerando 9 espacios usados ya que el Enter (0xA) esta el la posición temporal[8] y en temporal[9] esta el byte nulo '\0'
4to caso de la imagen: Al ingresar exactamente 7 letras A, strlen marca una longitud de 8 espacios mismo caso que el anterior sigue considerando el "Enter" como un valor valido de la cadena.

En conclusión, si al usar fgets correctamente, el valor de nuestro arreglo en su posición strlen(temporal) - 1 es igual 0xA entonces no queda nada en nuestro buffer de STDIN, en caso contrario si queda Algo, ya sea solo el Enter o Texto restante + Enter.

En cualquier caso después de realizar esta evaluación si detectamos el caso de que aun quede buffer pendiente por leer, podemos leerlo asi:

while ((c = getchar()) != 0xA);


Saludos
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

K-YreX

Y si por ejemplo tienes datos pendientes en el buffer pero no sabes que datos son, osea que no tiene porqué ser un salto de línea (0xa), has encontrado alguna forma de vaciar el buffer??
Es decir igual que:
Cita de: AlbertoBSD en 24 Noviembre 2018, 21:50 PM
while ((c = getchar()) != 0xA);
Pero como representar el final de datos pendientes. No sé si me he explicado.
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

AlbertoBSD

Realmente no importa lo que queda en el buffer. Ya que si vaciamos el buffer de stdin no se volvera a llenar hasta que volvamos a llamar a una funcion tipo fgets, gets, scanf... etc.

Ejemplo:


#include<stdio.h>
#include<string.h>

int main() {
int len,i = 0;
char temporal[10];
char c;
printf("Ingrese una cadena: ");
fgets(temporal,10,stdin);
printf("Cadena Leida: %s\n",temporal);
len = strlen(temporal);
if(temporal[len - 1 ]  != 0xA) {
while ((c = getchar()) != 0xA);
}
printf("Ingrese otra cadena: ");
fgets(temporal,10,stdin);
printf("Cadena Leida: %s\n",temporal);
}


Salida:



Saludos
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

MAFUS

A raíz de este post me he puesto a pensar en todo esto.
Ya que C viene del UNIX y en UNIX todo son archivos y por tanto stdin es un archivo ¿qué tal si llevamos el puntero de stdin hasta el final y nos saltamos todo lo que hay dentro?

Al menos en MinGW64 funciona. Faltarían más pruebas con otras bibliotecas y S.O. para corroborar.

#include <stdio.h>

int main() {
    char c;
    while(1) {
        printf("> ");
        c = getchar();
        fseek(stdin, 0, SEEK_END);
        printf("Caracter: %c\n", c);
    }
}


Incluso acepta una única pulsación de intro.

AlbertoBSD

Cita de: MAFUS en 24 Noviembre 2018, 22:34 PM
A raíz de este post me he puesto a pensar en todo esto.
Ya que C viene del UNIX y en UNIX todo son archivos y por tanto stdin es un archivo ¿qué tal si llevamos el puntero de stdin hasta el final y nos saltamos todo lo que hay dentro?

Incluso acepta una única pulsación de intro.

;-) ;-)

No habia intentado eso, pero funciona muy bien!, yo intente con la funcion feof para tratar de determinar si estabamos en el final del archivo, pero no funciono para STDIN.

Con fseek si funciona!!!


#include<stdio.h>

int main() {
char temporal[10];
printf("Ingrese una cadena: ");
fgets(temporal,10,stdin);
printf("Cadena Leida: %s\n",temporal);
fseek(stdin, 0, SEEK_END);
printf("Ingrese otra cadena: ");
fgets(temporal,10,stdin);
printf("Cadena Leida: %s\n",temporal);
}





Saludos!
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

Beginner Web

Despues lo aplico Alberto, ahora no hay tiempo  ;-)
<3
7w7

Eternal Idol

Cita de: Beginner Web en 26 Noviembre 2018, 01:30 AM
Despues lo aplico Alberto, ahora no hay tiempo  ;-)
<3

No hagas spam por favor y de paso acorta tu firma, escribis una linea y ocupas media pantalla.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

SDCC

#9
Disculpen por revivir el tema...Con referencia a la solucion con ayuda de la funcion fseek me ha surgido la siguiente duda.Investigando sobre la funcion he visto que sus valores de retorno debera ser 0 para verificar que realmente ha realizado su tarea de manera correcta a partir de esto he intentado complementar un poco el ejemplo que se mostraba al principio.


#include <stdio.h>
   
       int main() {
           char c;
           int flagValidacion ;
           while(1) {
               printf("> ");
               c = getchar();
               flagValidacion = fseek(stdin, 0, SEEK_END);
               if(flagValidacion)
                printf("Error!!\n");
               printf("Caracter: %c\n", c);
           }
       }


A partir de lo anterior efectivamente la funcion limpiaba el buffer sin embargo su valor de retorno me indicaba que no ha cumplido su tarea de manera adecuada debido a que retornaba un valor distinto de 0.He intentado esto con un simple archivo de texto y me ha funcionado de manera correcta,incluso experimentando un poco mas he notado que si yo llamo la funcion ftell despues de realizar fseek(stdin,0,SEEK_END)  siempre me va devolver 0 mientras que en un archivo de texto comun realmente me regresa la posicion final de mi archivo.Disculpen si cometo algun error pero tengo dudas sobre que es lo que realmente esta haciendo debido a yo pensaba que con ayuda de fseek simplemente nos saltariamos hasta el final del archivo pero eso no significa que nosotros  efectivamente borremos todo lo que estaba contenido anteriormente, si esto es cierto entonces ¿es posible que en algun momento tengamos tantos datos que nos hemos saltado en el stdin que nuestro programa colapse?