如何在获取新输入之前清除标准输入?

问题描述 投票:0回答:6

我已经阅读了大约 5-10 个如何清除标准输入的不同建议,但没有一个适合我的需要。问题是

fflush(stdin)
在我的电脑上运行得很好,但不幸的是它似乎并不在所有地方都能运行,所以我需要具有相同功能的东西。我尝试的所有其他方法都会在标准输入不为空时清除它,但在标准输入为空时需要用户输入,这意味着它需要在我不想得到任何输入的那一刻输入(+它无论如何都会丢弃它)。

问题是:在需要用户输入之前,我能否以某种方式确保

stdin
为空? (如果没有,那么然后才以某种方式清除它?) 比如:

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

编辑:问题是我从

stdin
一个接一个地加载字符,在某些时候,上一次迭代的部分输入可能会或可能不会被丢弃。无论哪种方式,在要求用户处理另一个输入之前,我都需要明确
stdin
。清除缓冲区本身并不是什么大问题,问题是当程序到达清除点时输入为空时会发生什么
stdin
,因为在那一刻程序需要另一个输入,该输入将被吃掉清算功能。这就是我想要摆脱的。 (当我可以使用
fflush(stdin);
时,我就知道,对于程序的下一行,无论如何,
stdin
都将是空的,没有任何问题......)

c stdin getchar
6个回答
7
投票

如何在获取新输入之前清除标准输入?
..所以我需要具有相同功能的东西。

对于便携式 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;
}

1
投票

来自类似的问题,使用poll(),将fds.fd设置为0(标准输入),fds.events设置为POLLIN,nfds设置为1,超时设置为零。调用 poll() 后,如果缓冲区为空,则 fds.revents 将设置为零,否则设置为 POLLIN。

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

此解决方案适用于 posix 兼容系统,但不适用于 Windows。使用 select() 实现可移植性。


0
投票

TL;DR

fflush(stdin)
根据标准调用未定义的行为,你永远不应该使用它。


来到您的代码(逻辑),您可以查找

EOF
,而不是寻找换行符。它没有先决条件,即在运行此循环之前
stdin
应该有 some 输入。

类似的东西

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

应该可以满足您的需求。


0
投票

仅使用

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

0
投票

标准 C 中 getline 的以下实现展示了如何清除 stdin :

/* read a line into s, return length */
int getline (char line[], int max_length)
{
  int i;
  char c;
  for (i = 0; i < max_length && (c = getchar()) != '\n'; ++i)
    line[i] = c;
  line[i] = '\n';
  line[i+1] = '\0';

  /* clear stdin if not empty (user entry exceeded max_length */
  if (c != '\n')
    while (getchar() != '\n')
      ;

  return i;
}

-4
投票

select
模块提供了一个名为
select
的功能,它可以准确实现您正在寻找的功能。
select.select
采用三个参数:

select.select(rlist, wlist, xlist)

每个参数应该是文件描述符列表(例如

[sys.sdtin]
),然后等待特定的 IO 操作可用。 IO 操作是给定文件描述符上的 read、write 或其他一些 exception。它返回一个
tuple
对应的列表,其中填充了准备好的文件描述符。

因此,如果

sys.stdin
有输入等待,那么该函数的行为将如下所示:

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

这本身并不能解决您的问题,因为默认情况下该函数将等待 IO 操作可用。然而,重要的是,

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中工作。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.