【问题标题】:Is this the proper way to flush the C input stream?这是刷新 C 输入流的正确方法吗?
【发布时间】:2016-12-04 11:49:00
【问题描述】:

我在谷歌和这里做了很多关于如何正确刷新输入流的搜索。我听到的只是关于 fflush() 如何为输入流未定义的混合争论,有人说就那样做,而其他人只是说不要这样做,我没有太多运气找到一个明确的效率/这样做的正确方式,大多数人都同意..我在编程方面很新,所以我还不知道该语言的所有语法/技巧,所以我的问题是哪种方式是最有效/最合适的解决方案清除C输入流??

  1. 在我尝试接收更多输入之前使用两次getchar()

  2. 只在输入上使用fflush() 函数?或

  3. 这就是我认为我应该这样做的方式。

    void clearInputBuf(void);  
    void clearInputBuf(void) 
    { 
          int garbageCollector; 
          while ((garbageCollector = getchar()) != '\n' && garbageCollector != EOF) 
          {}
    }
    

所以,每当我需要读取新的 scanf() 或使用 getchar() 暂停程序时,我只需调用 clearInputBuf.. 那么三种解决方案中最好的方法是什么,或者有更好的方法吗?选项?

【问题讨论】:

  • 不简单。这就是为什么有像GNU Readline 这样的库来处理输入的原因,因为使用库函数很难做到这一点。
  • 对于任何阅读此作为解决方案的人来说,这只是一个旁注!还有其他方法可以更有效地获取输入,但如果您只是学习,这很可能会为您提供足够好的服务,直到您更好地掌握语言!但是我要提到的一件事是,调用 clearInputBuf() 会导致无限循环,直到您将某些内容输入其中,这意味着如果您在它为空时调用它,即使下一次调用是 printf(),它也不会超过调用,直到您给它喂点东西..
  • @Programming_n00b:您可以通过单击分数下方的灰色复选标记来接受其中一个答案。

标签: c flush fflush


【解决方案1】:

我听到的只是关于 fflush() 如何为输入流未定义的混合争论

没错。不要使用fflush() 刷新输入流。

(A) 适用于在输入流中留下单个字符的简单情况(例如 scanf() 留下换行符)。

(B) 不要使用。它是在某些平台上定义的。但不要依赖于标准所称的未定义行为

(C) 显然是 3 个选项中最好的,因为它可以“刷新”输入流中的任意数量的字符。

但如果您阅读(例如使用fgets()),您可能不需要清除输入流。

【讨论】:

  • 使用fgets() 读取行仍然会导致行过长的问题。
  • @chqrlie 这就是为什么我说“可能更少需要”(使用足够长的缓冲区 - 可以根据输入的读取知识合理猜测/控制)而不是说“不需要"。
【解决方案2】:

这取决于您对“刷新输入流”的看法。

对于输出流,刷新操作确保所有写入流但在内存中缓冲的数据都已刷新到底层文件系统文件。这是一个非常明确的操作。

对于输入流,没有明确定义的刷新流应该做什么的操作。我会说这没有任何意义。

C 标准库的一些实现将输入流的“刷新”的含义重新定义为“清除当前行中尚未读取的其余部分”。但这完全是任意的,其他实现选择什么都不做。

从 C11 开始,这种差异已得到纠正,标准现在明确指出 fflush() 函数不适用于输入流,正是因为它没有意义,而且我们不希望每个运行时库供应商都去实现以他们喜欢的任何方式。

所以,请务必按照您的方式实现您的 clearInputBuf() 函数,但不要将其视为“刷新输入流”。没有这样的东西。

【讨论】:

    【解决方案3】:

    结果是平台相关的。

    fflush() 不能将输入流作为参数,因为根据 c 标准,这是未定义的行为,因为该行为未在任何地方定义。

    • 在 Windows 上,fflush() 有一个已定义的行为,它会执行您需要它执行的操作。

    • 在 Linux 上,fpurge(3) 也可以执行您希望它执行的操作。

    最好的方法是简单地循环读取所有字符,直到

    1. 找到换行符。
    2. EOFgetchar() 返回。

    喜欢你的 clearInputBuf() 函数。

    请注意,刷新输出流意味着将所有未写入的数据写入流,即仍在缓冲区中等待刷新的数据。但是从流中读取所有未读字节,并没有相同的含义。

    这就是为什么 fflush() 输入流没有意义。另一方面,fpurge() 是专门为此设计的,它的名称是一个更好的选择,因为您想清除输入流并重新开始。问题是,它不是标准函数。

    阅读fpurge(3) 应该会阐明为什么fflush(stdin) 是未定义的行为,以及为什么像Windows 上的实现这样的实现没有意义,因为它使fflush() 在不同的输入下表现不同。这就像使 c 与 PHP 兼容。

    【讨论】:

    • 很高兴了解fpurge(),但它并不能完全解决问题:如果输入文件(即终端)中有更多可用数据可以放入FILE 缓冲区, fpurge() 只会丢弃从系统读取的内容,而不是仍在终端系统缓冲区中徘徊的内容。 while ((c = getchar()) != EOF && c != '\n'){} 解决方案似乎更可靠。
    • @chqrlie 因此,这是最好的方法。
    【解决方案4】:

    问题比看起来更微妙:

    • 在具有精细终端设备的系统(例如 unix 和 OS/X)上,来自终端的输入在 2 个单独的级别进行缓冲:系统终端使用缓冲区来处理行编辑,从仅使用退格键更正输入到使用光标和控制键进行整行编辑。这称为模式。在输入回车键或输入文件结束组合键之前,系统会缓冲一整行输入。

    • FILE 函数执行它们自己的缓冲,对于与终端关联的流,默认情况下是行缓冲。默认情况下,缓冲区大小设置为BUFSIZ,当缓冲内容被消耗时,系统会请求字节。对于大多数请求,会从系统将整行读取到流缓冲区中,但在某些情况下,例如当缓冲区已满时,当scanf() 返回时,只会从系统中读取部分行。这就是为什么丢弃流缓冲区的内容可能并不总是足够的原因。

    刷新输入缓冲区可能有不同的含义:

    • 丢弃用户为响应getchar()fgets()scanf() 等输入请求而输入的额外输入,包括换行符。在scanf() 的情况下,flushing 这个输入的需要尤其明显,因为大多数格式行不会导致换行符被消耗。

    • 丢弃任何待处理的输入并等待用户按键。

    对于第一种情况,您可以可移植地实现 Fluch 函数:

    int flush_stream(FILE *fp) {
        int c;
        while ((c = getc(fp)) != EOF && c != '\n')
            continue;
        return c;
    }
    

    这正是您的clearInputBuf() 函数为stdin 所做的。

    对于第二种情况,没有可移植的解决方案,系统特定的方法也很重要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-30
      • 2013-07-13
      • 1970-01-01
      • 2014-05-05
      • 2013-09-25
      • 1970-01-01
      • 2013-02-18
      • 1970-01-01
      相关资源
      最近更新 更多