Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: AlbertoBSD en 24 Noviembre 2018, 18:54 pm



Título: ¿Queda algo en el Buffer de Entrada stdin? (Solucionado)
Publicado por: AlbertoBSD en 24 Noviembre 2018, 18:54 pm
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)
Código
  1. #include<stdio.h>
  2. #include<string.h>
  3.  
  4. int main() {
  5. int len,i = 0;
  6. char temporal[10];
  7. char c;
  8. printf("Ingrese una cadena: ");
  9. fgets(temporal,10,stdin);
  10. printf("Cadena Leida: %s\n",temporal);
  11. len = strlen(temporal);
  12. if(temporal[len - 1 ]  != 0xA) {
  13. while ((c = getchar()) != 0xA);
  14. }
  15. printf("Ingrese otra cadena: ");
  16. fgets(temporal,10,stdin);
  17. printf("Cadena Leida: %s\n",temporal);
  18. }
  19.  


con fseek:


Código
  1. #include <stdio.h>
  2.  
  3.    int main() {
  4.        char c;
  5.        while(1) {
  6.            printf("> ");
  7.            c = getchar();
  8.            fseek(stdin, 0, SEEK_END);
  9.            printf("Caracter: %c\n", c);
  10.        }
  11.    }


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

Código
  1. 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? (https://foro.elhacker.net/programacion_cc/iquestcomo_filtrar_todo_tipo_de_datos_de_entrada_en_un_programa-t489983.0.html)

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

Código
  1. #include<stdio.h>
  2. #include<string.h>
  3.  
  4. int main() {
  5. char temporal[10];
  6. printf("Ingrese menos de 10 o mas de 10 caracteres\npara ver el comportamiento de feof(stdin): ");
  7. fgets(temporal,10,stdin);
  8. temporal[strcspn(temporal,"\n\r")] = '\0';
  9. printf("Valor leido %s\n",temporal);
  10. if(feof(stdin)) {
  11. printf("stdin llego al final del archivo\n");
  12. }
  13. else {
  14. printf("stdin NO llego al final del archivo\n");
  15. }
  16. }
  17.  

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.

(https://i.imgur.com/iFM60zl.png)


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

Código:
#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:

(https://i.imgur.com/A53rMCP.png)

Codigo:

Código
  1. #include<iostream>
  2.  
  3. using namespace std;
  4. int main() {
  5. int numero_1,numero_2;
  6. cout<<"Ingrese un numero: ";
  7. cin>>numero_1;
  8. cout<<"Ingrese un numero: ";
  9. cin>>numero_2;
  10. cout<<"Valor leido "<<numero_1<<endl;
  11. cout<<"Valor leido "<<numero_2<<endl;
  12. }
  13.  

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


Título: Re: ¿Queda algo en el Buffer de Entrada stdin?
Publicado por: K-YreX en 24 Noviembre 2018, 19:14 pm
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


Título: Re: ¿Queda algo en el Buffer de Entrada stdin?
Publicado por: AlbertoBSD en 24 Noviembre 2018, 21:50 pm
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:

Código
  1. #include<stdio.h>
  2. #include<string.h>
  3.  
  4. int main() {
  5. int len,i = 0;
  6. char temporal[10];
  7. printf("Ingrese una cadena: ");
  8. fgets(temporal,10,stdin);
  9. len = strlen(temporal);
  10. printf("Cadena Leida: %s",temporal);
  11. printf("La longitud de la cadena leida es de %i\n",len);
  12. while(i<10) printf("%.2X",temporal[i++]);
  13. printf("\n");
  14. }
  15.  
  16.  

Aqui unas cuantas salidas.

(https://i.imgur.com/QMYoyE7.png)

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:

Código
  1. while ((c = getchar()) != 0xA);


Saludos


Título: Re: ¿Queda algo en el Buffer de Entrada stdin?
Publicado por: K-YreX en 24 Noviembre 2018, 22:09 pm
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:
Código
  1. while ((c = getchar()) != 0xA);
Pero como representar el final de datos pendientes. No sé si me he explicado.


Título: Re: ¿Queda algo en el Buffer de Entrada stdin?
Publicado por: AlbertoBSD en 24 Noviembre 2018, 22:16 pm
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:

Código
  1. #include<stdio.h>
  2. #include<string.h>
  3.  
  4. int main() {
  5. int len,i = 0;
  6. char temporal[10];
  7. char c;
  8. printf("Ingrese una cadena: ");
  9. fgets(temporal,10,stdin);
  10. printf("Cadena Leida: %s\n",temporal);
  11. len = strlen(temporal);
  12. if(temporal[len - 1 ]  != 0xA) {
  13. while ((c = getchar()) != 0xA);
  14. }
  15. printf("Ingrese otra cadena: ");
  16. fgets(temporal,10,stdin);
  17. printf("Cadena Leida: %s\n",temporal);
  18. }
  19.  

Salida:

(https://i.imgur.com/YEZ93TW.png)

Saludos


Título: Re: ¿Queda algo en el Buffer de Entrada stdin?
Publicado por: 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?

Al menos en MinGW64 funciona. Faltarían más pruebas con otras bibliotecas y S.O. para corroborar.
Código
  1. #include <stdio.h>
  2.  
  3. int main() {
  4.    char c;
  5.    while(1) {
  6.        printf("> ");
  7.        c = getchar();
  8.        fseek(stdin, 0, SEEK_END);
  9.        printf("Caracter: %c\n", c);
  10.    }
  11. }

Incluso acepta una única pulsación de intro.


Título: Re: ¿Queda algo en el Buffer de Entrada stdin?
Publicado por: AlbertoBSD en 25 Noviembre 2018, 00:02 am
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!!!

Código
  1. #include<stdio.h>
  2.  
  3. int main() {
  4. char temporal[10];
  5. printf("Ingrese una cadena: ");
  6. fgets(temporal,10,stdin);
  7. printf("Cadena Leida: %s\n",temporal);
  8. fseek(stdin, 0, SEEK_END);
  9. printf("Ingrese otra cadena: ");
  10. fgets(temporal,10,stdin);
  11. printf("Cadena Leida: %s\n",temporal);
  12. }
  13.  


(https://i.imgur.com/g3i4w5X.png)

Saludos!


Título: Re: ¿Queda algo en el Buffer de Entrada stdin?
Publicado por: Beginner Web en 26 Noviembre 2018, 01:30 am
Despues lo aplico Alberto, ahora no hay tiempo  ;-)
<3


Título: Re: ¿Queda algo en el Buffer de Entrada stdin?
Publicado por: Eternal Idol en 29 Noviembre 2018, 10:47 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.


Título: Re: ¿Queda algo en el Buffer de Entrada stdin? (Solucionado)
Publicado por: SDCC en 7 Diciembre 2018, 04:35 am
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.

Código
  1. #include <stdio.h>
  2.  
  3.        int main() {
  4.            char c;
  5.            int flagValidacion ;
  6.            while(1) {
  7.                printf("> ");
  8.                c = getchar();
  9.                flagValidacion = fseek(stdin, 0, SEEK_END);
  10.                if(flagValidacion)
  11.                 printf("Error!!\n");
  12.                printf("Caracter: %c\n", c);
  13.            }
  14.        }
  15.  

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?


Título: Re: ¿Queda algo en el Buffer de Entrada stdin? (Solucionado)
Publicado por: AlbertoBSD en 7 Diciembre 2018, 05:49 am
Tienes razon por alguna razon el fseek no esta procesando la ultima parte del  stdin. Cambie el printf por perror para ver que error marca y dice

Código
  1. if(flagValidacion)
  2. perror("fseek");

Código:
fseek: Invalid argument

Hay que ver el source code de fseek y ver exactamente que parte es la que falla. Y ver si es que de alguna manera el sistema guarda esos bytes saltados, cosas que no creo. Pero hay que revisar.

La otra opcion es lo que comente de revisar el ultimo valor leido por fgets y determinar si queda algo en buffer de entrada. Ejemplo:

Código
  1. #include <stdio.h>
  2.  
  3. void myflush(const char *ptr,int size) {
  4. int i = 0;
  5. while(i < size && ptr[i++] != 0xA);
  6. if(i == size)
  7. while (getchar() != 0xA);
  8. }
  9.  
  10. int main() {
  11. char temp[10];
  12. int flagValidacion ;
  13. while(1) {
  14. printf("> ");
  15. fgets(temp,10,stdin);
  16. myflush(temp,10); // Tiene que ser el ultimo arreglo leido y la ultima cantidad marcada con fgets
  17. printf("cadena: %s\n", temp);
  18. }
  19. }
  20.  

O bien realizarlo con una implementacion propia de fgets que le quite ese "pendiente" al programador. Incluso como un extra le borras al final ese "Enter" si es que existe.

Código
  1. #include <stdio.h>
  2.  
  3. void myfgets_stdin(char *ptr,int size) {
  4. int i = 0;
  5. fgets(ptr,size,stdin);
  6. while(i < size && ptr[i++] != 0xA);
  7. if(i == size)
  8. while (getchar() != 0xA);
  9. else
  10. ptr[i-1] = '\0';
  11. }
  12.  
  13. int main() {
  14. char temp[10];
  15. int flagValidacion ;
  16. while(1) {
  17. printf("> ");
  18. myfgets_stdin(temp,10);
  19. printf("cadena: %s\n", temp);
  20. }
  21. }
  22.  

Saludos



Título: Re: ¿Queda algo en el Buffer de Entrada stdin? (Solucionado)
Publicado por: MAFUS en 7 Diciembre 2018, 09:46 am
stdin no és un archivo de texto al uso. Se ve como archivo por la forma en que UNIX maneja los componentes de la máquina (cómo se ha dicho C se creó para programar UNIX y tiene esas reminiscencias).

stdin es un stream así que no tiene final: se vacía a petición del S.O. pero se llena por circunstancias ajeno a él. De alguna forma siempre se está en el inicio de stdin.

Al decirle a fseek que vaya al final de stdin lo que se hace es ir al final del buffer, entiéndase el final en ese justo momento, pero lógicamente se vuelve a encontrar en su posición inicial listo para llenarse de nuevo con cada nuevo dato que se entre.

Imaginaros una cola se escribe por el final y se lee por el principio. Ahora imaginaros que podéis mover el puntero de lectura con fseek al final. Todo lo que hay al inicio desaparece y la cola aparece vacía.