¿Cómo borrar stdin antes de obtener una nueva entrada?

8 minutos de lectura

avatar de usuario
Tomás

He leído entre 5 y 10 consejos diferentes sobre cómo borrar la entrada estándar, pero ninguno se adapta a mis necesidades. La cosa es que fflush(stdin) funcionó perfectamente en mi computadora, pero desafortunadamente no parece funcionar en todas partes, así que necesito algo con la misma funcionalidad. De todas las demás formas que probé, borra el stdin cuando no está vacío, pero requiere la entrada del usuario cuando el stdin ESTÁ vacío, lo que significa que requiere entrada en un momento en el que no quiero obtener ninguno (+ lo descarta de todos modos).

La pregunta es: ¿Puedo de alguna manera asegurarme de que stdin ¿ESTÁ vacío antes de que requiera la entrada del usuario? (y si no, ENTONCES, ¿y solo entonces borrarlo de alguna manera?) algo como:

if (stdin is NOT empty) 
    while (getchar() != '\n')
        continue;

EDIT: la cosa es que cargo caracteres de stdin uno por uno y en algún momento, una parte de la entrada de la iteración anterior puede descartarse o no. de cualquier manera, necesito tener claro stdin antes de pedirle al usuario que se procese otra entrada. Borrar el búfer en sí no es gran cosa, el problema es lo que sucede cuando la entrada está vacía cuando el programa llega al punto de borrar stdin, porque en ese momento el programa necesita otra entrada que va a ser devorada por la función de limpieza. De eso quiero deshacerme. (cuando podía usar fflush(stdin); Solo sabía que para la siguiente línea de mi programa, el stdin ESTARÁ vacío pase lo que pase, sin hacer preguntas…)

  • fflush(stdin) es UB.

    – Sourav Ghosh

    19/04/2016 a las 10:00

  • No existe tal cosa como un flujo de E/S vacío es un Arroyo. Considerar: ./myprog </dev/random ; ¿alguna vez verá el final de su transmisión estándar?

    – joop

    19 de abril de 2016 a las 10:28


  • programandolo en windows, tiene que funcionar en linux…

    – Tomás

    19 de abril de 2016 a las 11:18

  • ¿Desea eliminar también cualquier línea de entrada escrita parcialmente (es decir, entrada para la cual el usuario no ha escrito ENTER para confirmar)?

    –Mark Plotnick

    19/04/2016 a las 17:37

  • @Jens El apt-get programa hace esto, intencionalmente. Ver Evite que sudo, apt-get, etc. traguen la entrada pegada a STDIN

    –Mark Plotnick

    20 de abril de 2016 a las 15:21

avatar de usuario
chux – Reincorporar a Monica

¿Cómo borrar stdin antes de obtener una nueva entrada?
.. así que necesito algo con la misma funcionalidad.

Con C portátil esto no es posible.


En su lugar, sugiera un paradigma C diferente (y más habitual):
Asegurar que las funciones de entrada anteriores consuman todos la entrada anterior.

fgets() (o *nix getline()) es el enfoque típico y resuelve la mayoría de las situaciones.

O enrolle el suyo propio. Lo siguiente lee una línea completa, pero no guarda la entrada adicional.

int mygetline(char *buf, size_t size) {
  assert(size > 0 && size <= INT_MAX);
  size_t i = 0;
  int ch;
  while ((ch = fgetc(stdin)) != EOF) {  // Read until EOF ...
    if (i + 1 < size) {
      buf[i++] = ch;
    }
    if (ch == '\n') {  // ... or end of line
      break;  
    }
  } 
  buf[i] = '\0';
  if (i == 0) { 
    return EOF;
  }
  return i;
}

De una pregunta similar, use encuesta() con fds.fd establecido en 0 (stdin), fds.events establecido en POLLIN, nfds establecido en 1 y timeout establecido en cero. Después de llamar a poll(), fds.revents se establecerá en cero si el búfer está vacío y en POLLIN en caso contrario.

struct pollfd fds = {0, POLLIN, 0};
poll(&fds, 1, 0);
if(fds.revents == POLLIN}
    printf("stdin buffer is not empty");

Esta solución funcionará en sistemas compatibles con posix, pero no en Windows. Utilizar Seleccione() para la portabilidad.

avatar de usuario
Sourav Ghosh

TL;RD fflush(stdin) invoca comportamiento indefinido según el estándar, nunca debe usarlo.


En cuanto a su código (lógica), en lugar de buscar una nueva línea, puede buscar EOF. No tiene un requisito previo que stdin debería tener algunos entrada antes de ejecutar este bucle.

Algo como

 while (getchar() != EOF);   //; is not a mistake

debe satisfacer sus necesidades.

  • while (getchar() != EOF); me pone en un bucle infinito que requiere otra y otra entrada. También probé if (!feof(stdin)) etc. pero nada de eso funcionó. Parece que stdin no tiene ningún EOF establecido, solo requiere otra entrada cuando se alcanza el final.

    – Tomás

    19 de abril de 2016 a las 10:23

  • Al menos en Windows, debe generar EOF explícitamente para stdio con Ctrl + Z.

    – Santo Gato Negro

    19 de abril de 2016 a las 11:20

  • Eh, no. Su bucle espera hasta que el usuario genera un EOF (^D o ^Z), mientras que el OP solicitó una forma de vaciar el búfer de entrada.

    – jch

    19 de abril de 2016 a las 15:18

avatar de usuario
pmg

Utilizar solamente fgets() leer stdin.

Use un búfer lo suficientemente grande y/o pruebe líneas completas.

Utilizando fgets() nunca tendrá que preocuparse por los caracteres adicionales en stdin.

// read characters until 'X'
while (((ch = getchar()) != EOF) && (ch != 'X')) putchar(ch);
// discard X and the rest of the line
fflush(stdin); // UB except for Windows

// read full line
char tmp[1000], *p;
if (!fgets(tmp, sizeof tmp, stdin)) /* deal with error */;
if (!*tmp) /* embedded NUL detected: input is not a text file */;
if (tmp[strlen(tmp) - 1] != '\n') /* partial line */;
p = tmp;
while (*p && *p != 'X') putchar(*p++);
// ignore the X and all the subsequent characters

avatar de usuario
revelar

los select módulo ofrece una función llamada select que logra exactamente lo que estás buscando. select.select toma tres argumentos:

select.select(rlist, wlist, xlist)

Cada argumento debe ser una lista de descriptores de archivo (como [sys.sdtin]) y luego espera hasta que una operación de E/S específica esté disponible. Las operaciones IO son rcabeza, writo o algún otro eXcepción en los descriptores de archivo dados. devuelve un tuple de listas correspondientes pobladas con los descriptores de archivo que están listos.

Entonces, si hay una entrada esperando en sys.stdin entonces la función se comportaría así:

>>> import select
>>> import sys
>>>
>>> select.select([sys.stdin], [], [])
([sys.stdin], [], [])
>>>

Por sí solo, esto no resuelve su problema porque, de forma predeterminada, la función esperará hasta que haya una operación de E/S disponible. Sin embargo, es importante destacar que select.select tiene una opción timeout argumento que denota cuánto tiempo esperará antes de darse por vencido. Simplemente tenemos que establecer el tiempo de espera en cero y podemos verificar la entrada sin bloquear el flujo del programa.

Veamos un ejemplo donde no hay entrada esperando en sys.stdin:

>>> import select
>>> import sys
>>>
>>> timeout = 0
>>> select.select([sys.stdin], [], [], timeout)
([], [], [])
>>>

Sabiendo que solo queremos el primer elemento de esa tupla (los flujos de entrada), estamos listos para hacer una declaración if útil:

if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
    print('Input is waiting to be read.')

Eso significa que borrar el flujo de entrada solo necesita alguna iteración:

while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
    sys.stdin.readline()

Y, por supuesto, podemos usar esto en cualquier flujo de entrada, así que pongámoslo en una función:

def clear_input(stream, timeout=0):
    '''Takes an input stream and discards each line in the buffer.
    The given timeout denotes how long in seconds to wait for 
    further input when none is available.
    '''
    while stream in select.select([stream], [], [], timeout)[0]:
        stream.readline()

Así que demostremos nuestra función para lograr lo que pides en tu pregunta:

import select
import sys
import time

def clear_input(stream, timeout=0):
    while stream in select.select([stream], [], [], timeout)[0]:
        stream.readline()

if __name__ == '__main__':
    print('Type some lines now. They will be ignored.')
    time.sleep(5)

    print('Clearing input.')
    clear_input(sys.stdin)

    user_input = raw_input('Please give some fresh input: ')
    print(user_input)

los clear_input La función se puede usar como una forma sin bloqueo para borrar los flujos de entrada y debería funcionar en Python2 y Python3.

  • Pero tu hizo ¿Te das cuenta de que esta es una pregunta de C, no una pregunta de Python?

    – Jens

    19 de abril de 2016 a las 15:08

  • Gran respuesta, sin embargo. Solo necesita una pregunta diferente.

    –Mark Plotnick

    19 de abril de 2016 a las 15:23

  • Supongo que la respuesta de TLDR es “usar Python” 🙂

    – dsclose

    19 de abril de 2016 a las 15:24

  • Estaba buscando una solución de Python. Desafortunadamente, esto no funciona en Windows. Recibo: OSError: [WinError 10038] An operation was attempted on something that is not a socket.

    – howdoicode

    20 de julio de 2020 a las 18:43

  • @howdoicode parece que necesita esta respuesta: stackoverflow.com/a/46811177

    – dsclose

    22 de julio de 2020 a las 4:16

  • Pero tu hizo ¿Te das cuenta de que esta es una pregunta de C, no una pregunta de Python?

    – Jens

    19 de abril de 2016 a las 15:08

  • Gran respuesta, sin embargo. Solo necesita una pregunta diferente.

    –Mark Plotnick

    19 de abril de 2016 a las 15:23

  • Supongo que la respuesta de TLDR es “usar Python” 🙂

    – dsclose

    19 de abril de 2016 a las 15:24

  • Estaba buscando una solución de Python. Desafortunadamente, esto no funciona en Windows. Recibo: OSError: [WinError 10038] An operation was attempted on something that is not a socket.

    – howdoicode

    20 de julio de 2020 a las 18:43

  • @howdoicode parece que necesita esta respuesta: stackoverflow.com/a/46811177

    – dsclose

    22 de julio de 2020 a las 4:16

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad