【问题标题】:How is std::iostream buffered?std::iostream 如何缓冲?
【发布时间】:2012-04-15 00:13:05
【问题描述】:

一般用例

我正在尝试实现一个基本的 shell。

说明

我需要读取用户输入,直到按下某些分隔符,以便执行相应的操作。这些分隔符可以是单个“a”、单个“b”或单个“c”。

输入示例如下所示(> 是 shell 提示符):

> 111-222-333-444a
Ok, '111-222-333-444' entered

为什么我需要内联分隔符而不是“换行”分隔符?

因为我想听诸如“向上箭头”之类的键盘事件来擦除当前命令并打印最后一个命令(实现历史功能)。

因为我想听'tabulation'之类的键盘事件来自动完成当前命令(实现自动完成功能)。

到目前为止我所拥有的

到目前为止,我的代码如下所示:

bool done = false;
char c;
while (!done && std::cin.get(c))
{   
    switch (c)
    {
    case 'a':
        // Do something corresponding to 'a'
        done = true;
        break;

    case 'b':
        // Do something corresponding to 'b'
        done = true;
        break;

    case 'c':
        // Do something corresponding to 'c'
        done = true;
        break;

    default:
        // buffer input until a delimiter is pressed
        break;
    }
}

但是,循环似乎只有在按下“换行”键后才会执行。这种行为扼杀了用户输入的交互本质。

问题是什么?

我知道 std::ostream 是缓冲的,所以在某些事件发生之前不会将内容写入磁盘,但是 std::istream 呢?缓冲了吗?如果是,它是怎么回事,我有什么选择来绕过这种行为?

另外,我将此问题标记为“家庭作业”,因为如果不是学校练习,这是我自己尝试做的练习,我不想只选择一个实现所有这玩意儿。

【问题讨论】:

  • 这个问题描述得很好,目标明确,背后的推理和示例代码。希望更多的人花时间这样做。

标签: c++ stl iostream


【解决方案1】:

如果您使用的是 POSIX 操作系统,您可以使用termios.h 中声明的函数和结构将终端设置为无缓冲。基本上你需要禁用规范输入,并将终端设置为非规范模式。这些链接可以帮助您了解两种终端模式之间的区别:

  1. Noncanonical Input(来自 libc 手册)

    非规范输入模式下,ERASE 和 KILL 等特殊编辑字符会被忽略。用于用户编辑输入的系统工具在非规范模式下被禁用,因此所有输入字符(除非它们是特殊的用于信号或流控制目的)都将完全按照键入的方式传递给应用程序。如果合适的话,由应用程序为用户提供编辑输入的方法。

  2. Canonical vs. non-canonical terminal input

    对于 canonical 输入 - 想想 shell;实际上,想想老式的 Bourne shell,因为 Bash 和亲戚都有命令行编辑。你输入一行输入;如果你犯了一个错误,你使用擦除字符(默认是退格,通常;有时是 DEL)来擦除前一个字符......对于 non-canonical 输入 - 想想 vi 或 vim 或......你按下一个字符,它立即可供程序使用。在你按下回车键之前,你不会被拦住。

  3. Description of Terminal Interface

    本章描述了用于控制异步通信端口的通用终端接口。它是否支持网络连接或同步端口或两者都取决于实现。

但本质上,您遇到的问题不在于 C++ iostream 接口本身,而与 C++ iostream 接口正在读取的控制终端的设置方式有关。因此,利用无缓冲 I/O 将是一个依赖于平台的操作,并且取决于您使用的是 Windows 还是实际的 POSIX 兼容平台(这包括用于 Windows 的 POSIX 环境,例如 Cygwin)而有所不同。

如果您发现弄乱终端设置问题太大,您还可以查看跨平台的curses programming library,例如PDCurses,它将抽象出底层终端类型的大部分复杂性。

【讨论】:

  • +1 这是终端缓冲的功能——与 iostream 没有直接关系。
  • 对,它非常依赖于平台。我在一台连接了数万个终端的大型机上工作。它只是不会被每个终端上的每一次击键所困扰。
【解决方案2】:

对于std::istream 是否以及如何缓冲的直接问题的答案是:是的,std::istream 是使用派生自 std::streambuf 的类的缓冲区,该类定义了具体源的实际缓冲和读取方法(或者,当使用std::ostream 作为目的地时)。这是否真的有任何缓冲取决于这个具体的类,它的操作通常是不可避免的。

也就是说,这不是你的问题!问题是,如果程序在按下换行键之前通常不会将输入发送到标准输入。这样一些行编辑可以由终端实现完成,而不必由每个程序完成。不幸的是,没有可移植的方法来改变这一点。在 POSIX 上,您可以使用 tcgetattr()tcsetattr() 将标准输入流(使用文件描述符 0)转换为非规范模式。我不知道如何在非 POSIX 系统上实现这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-16
    • 2015-08-26
    • 2018-08-20
    • 2013-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多