Как очистить stdin перед тем, как получить новый вход?

Я прочитал около 5-10 различных советов, как очистить stdin, но ни один из них не соответствует моим потребностям. Дело в том, что fflush(stdin) отлично работал на моем компьютере, но, к сожалению, он не работает везде, поэтому мне нужно что-то с той же функциональностью. Каждый другой способ, который я пробовал, очищает stdin, когда он не пуст, но требует ввода пользователем, когда stdin IS пуст, что означает, что он требует ввода в тот момент, когда я не хочу его получать (+ он все равно отбрасывает).

Вопрос: могу ли я как-то убедиться, что stdin IS empty, прежде чем я требую ввода пользователя? (а если нет, ТОГДА и только потом это как-то его очистить?) что-то вроде:

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

EDIT: дело в том, что я загружаю символы из stdin один за другим, и в какой-то момент часть ввода предыдущей итерации может или не может быть отброшена. в любом случае, мне нужно иметь четкий stdin, прежде чем я попрошу пользователя ввести другой вход для обработки. Очистка самого буфера не такая уж большая проблема, проблема в том, что происходит, когда вход пуст, когда программа доходит до точки очистки stdin, потому что в этот момент программе нужен другой вход, который будет съеден функция очистки. То, от чего я хочу избавиться. (когда я мог использовать fflush(stdin);, я просто знал, что для следующей строки моей программы stdin будет пустым, несмотря ни на что, никаких вопросов не задано...)

Ответ 1

Как очистить стандартный ввод перед новым вводом?
.. так что мне нужно что-то с той же функциональностью.

С портативным C это невозможно.


Вместо этого предложите другую (и более обычную C) парадигму:
Убедитесь, что предыдущие функции ввода потребляют все предыдущие данные.

fgets() (или * nix getline()) является типичным подходом и решает большинство ситуаций.

Или сверните свое собственное. Далее читается вся строка, но не сохраняется дополнительный ввод.

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;
}

Ответ 2

TL; DR fflush(stdin) вызывает undefined поведение в соответствии со стандартом, его никогда не следует использовать.


Приступая к вашему коду (логике), вместо поиска новой строки вы можете искать EOF. У этого нет предпосылок, что stdin должен иметь некоторый ввод перед запуском этого цикла.

Что-то вроде

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

должен соответствовать вашим потребностям.

Ответ 3

Используйте только fgets() для чтения stdin.

Используйте достаточно большой буфер и/или тест для полных строк.

Используя fgets(), вам больше не нужно беспокоиться о дополнительных символах в 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

Ответ 4

Модуль select предлагает функцию под названием select, которая обеспечивает именно то, что вы ищете. select.select принимает три аргумента:

select.select(rlist, wlist, xlist)

Каждый аргумент должен быть списком дескрипторов файлов (например, [sys.sdtin]), а затем ждет, пока не будет доступна конкретная операция ввода-вывода. Операции ввода-вывода - это r ead, w обряд или какой-либо другой e x ception на заданных файловых дескрипторах. Он возвращает tuple соответствующих списков, заполненных файловыми дескрипторами, которые готовы.

Итак, если в sys.stdin есть входное ожидание, тогда функция будет вести себя так:

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

Само по себе это не решает вашу проблему, потому что по умолчанию функция будет ждать, пока не будет доступна операция ввода-вывода. Важно, однако, select.select имеет необязательный аргумент timeout, обозначающий, как долго он будет ждать, прежде чем сдаться. Нам просто нужно установить тайм-аут на ноль, и мы можем проверить ввод, не блокируя поток программы.

Посмотрите пример, в котором нет ожидающего ввода в sys.stdin:

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

Зная, что нам нужен только первый элемент этого кортежа (входные потоки), мы готовы сделать полезный оператор if:

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

Это означает, что для очистки входного потока требуется определенная итерация:

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

И мы можем, конечно, использовать это для любого входного потока, поэтому давайте поместим его в функцию:

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()

Итак, давайте продемонстрируем нашу функцию для достижения того, о чем вы просите в своем вопросе:

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)

Функция clear_input может использоваться как неблокирующий способ очистки входных потоков и должна работать в Python2 и Python3.