关于C语言里清除输入缓冲

这个应该是个经常遇到的问题了,最近参与了C的比赛时又提起

常用的有fflush(stdin),不过这个实际上行为未定义,并且只在win下有效
同样还有rewind(stdin),自己是用都没用过,也不能跨平台。
在这种情况下,有linux爱好者推荐了跨平台setbuf(stdin,NULL),试了下,还是不得其要领
最后发现好用又不用动脑子的就两个:
while((c = getchar()) != '\n' && c != EOF)
scanf(“%*[^\n]%*c”)
看起来比较土但是的确很好用的说
深入解释看下面转的帖子
来自这里:http://www.cppblog.com/lucency/archive/2008/04/07/46419.html
========================================================================

声明:

本文很大部分内容来自APUE--UNIX环境高级编程。

1. 缓冲类型。

标准库提供缓冲是为了减少对read和write的调用。提供的缓冲有三种类型(整理自APUE):

  • 全缓冲。

在这种情况下,实际的I/O操作只有在缓冲区被填满了之后才会进行。对驻留在磁盘上的文件的操作一般是有标准I/O库提供全缓冲。缓冲区一般是在第一次对流进行I/O操作时,由标准I/O函数调用malloc函数分配得到的。

术语flush描述了标准I/O缓冲的写操作。缓冲区可以由标准I/O函数自动flush(例如缓冲区满的时候);或者我们对流调用fflush函数。

  • 行缓冲

在这种情况下,只有在输入/输出中遇到换行符的时候,才会执行实际的I/O操作。这允许我们一次写一个字符,但是只有在写完一行之后才做I/O操作。一般的,涉及到终端的流--例如标注输入(stdin)和标准输出(stdout)--是行缓冲的。

  • 无缓冲

标准I/O库不缓存字符。需要注意的是,标准库不缓存并不意味着操作系统或者设备驱动不缓存。

ISO C要求:

  • 当且仅当不涉及交互设备时,标准输入和标准输出是全缓存的。
  • 标准错误绝对不是全缓存的。

但是,这并没有告诉我们当标准输入/输出在涉及交互设备时,它们是无缓存的还是行缓存的;也没有告诉我们标准错误应该是行缓存的还是无缓存的。不过,大多数实现默认的缓存类型是这样的:

  • 标准错误总是无缓存的。
  • 对于所有的其他流来说,如果它们涉及到交互设备,那么就是行缓存的;否则是全缓存的。

2. 改变默认缓存类型

可以通过下面的函数改变缓存类型(摘自APUE):

void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);

这些函数必须在流打开之后、但是未对流做任何操作之前被调用(因为每个函数都需要一个有效的文件指针作为第一个参数)。

利用setbuf,可以打开或者关闭缓存。为了打开缓存,buf参数必须一个大小为BUFSIZ的缓存,BUFSIZ是定义在stdio。h中的常量。<<ISO/IEC 9899>>要求:BUFSIZ至少为256。如果要关闭缓存,可以将buf设成NULL。

利用setvbuf,我们可以设定缓存类型。这是通过mode参数指定的。

关于这两个函数,可以看下表(摘自APUE):

Function mode buf Buffer and length Type of buffering
setbuf non-null user buf of length BUFSIZ fully buffered or line buffered
NULL (no buffer) unbuffered
setvbuf _IOLBF non-null user buf of length size fully buffered
NULL system buffer of appropriate length
_IOFBF non-null user buf of length size line buffered
NULL system buffer of appropriate length
_IONBF (ignored) (no buffer) unbuffered

需要注意的是:如果在函数内为流分配了自动变量作为缓存,那么在退出之前需要将流关闭。因此最好让系统自己分配缓存,这些缓存在流关闭的时候会自动被释放。
3.如果清理输入缓存

关于这点可以参看comp.lang.c FAQ的Question12.26b:

Q: If fflush won't work, what can I use to flush input?

A: It depends on what you're trying to do. If you're trying to get rid of an unread newline or other unexpected input after calling scanf (see questions 12.18a-12.19), you really need to rewrite or replace the call to scanf (see question 12.20). Alternatively, you can consume the rest of a partially-read line with a simple code fragment like

while((c = getchar()) != '\n' && c != EOF)
;

(You may also be able to use the curses flushinp function.)

There is no standard way to discard unread characters from a stdio input stream. Some vendors do implement fflush so that fflush(stdin) discards unread characters, although portable programs cannot depend on this. (Some versions of the stdio library implement fpurge or fabort calls which do the same thing, but these aren't standard, either.) Note, too, that flushing stdio input buffers is not necessarily sufficient: unread characters can also accumulate in other, OS-level input buffers. If you're trying to actively discard input (perhaps in anticipation of issuing an unexpected prompt to confirm a destructive action, for which an accidentally-typed ``y'' could be disastrous), you'll have to use a system-specific technique to detect the presence of typed-ahead input; see questions 19.1 and 19.2. Keep in mind that users can become frustrated if you discard input that happened to be typed too quickly.

References: ISO Sec. 7.9.5.2
H&S Sec. 15.2

4. 几点需要注意的地方

  • 对输入流进行fflush操作是无定义的。
  • 无缓存并不意味着一个个的那样处理输入,而是说当操作系统返回它们时,对于标准库函数来说它们是立即可用的。因为还可能有操作系统级甚至是硬件级的缓存,这些并不是setbuf可以控制的。
  • 另外可以参考这里(我就是最先从这里开始看的)。还有这里。我从后面那个链接摘录一些重要的下来:

setbuf() has to do with the delivery of bytes between the
C library FILE* management layer and the OS I/O layer.

Calls to fread(), fgets(), fgetc(), and getchar() work within
whatever FILE* buffered data is available, and when that data
is exhausted, the calls request that the FILE* buffer be refilled
by the system I/O layer.

When full buffering is turned on, that refill operation results in the
FILE* layer requesting that the operating system hand it a full
buffer's worth of data; when buffering is turned off, that
refill operation results in the FILE* layer requesting that the
operating system return a single character.

...setting an input stream to be unbuffered
does NOT tell the operating system to tell the device driver
to go into any kind of "raw" single-character mode. There are
system-specific calls such as ioctl() and tcsetterm() that
control what the device driver will do.

发表评论

电子邮件地址不会被公开。

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>