如何缓冲标准::iostream

How is std::iostream buffered?

本文关键字:标准 iostream 缓冲 何缓冲      更新时间:2023-10-16

一般用例

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

描述

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

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

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

为什么我想要内联分隔符而不是"换行符"分隔符?

因为我想听键盘事件,例如"向上箭头"以擦除当前命令并打印最后一个命令(实现历史记录功能(。

因为我想听键盘事件,例如"制表"以自动完成当前命令(实现自动完成功能(。

到目前为止我有什么

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

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 呢?它是缓冲的吗?如果是,情况如何,我可以选择什么来绕过此行为?

另外,我将这个问题标记为"家庭作业",因为,如果不是学校练习,这是我自己尝试做的练习,我不想只选择一个实现所有这些东西的库。

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

  1. 非规范输入(来自 libc 手册(

    非规范输入模式下,将忽略特殊编辑字符,例如 ERASE 和 KILL。用户编辑输入的系统功能在非规范模式下被禁用,以便所有输入字符(除非它们用于信号或流量控制目的(完全按照键入的方式传递给应用程序。如果适用,由应用程序为用户提供编辑输入的方法。

  2. 规范
  3. 与非规范终端输入

    对于规范输入 - 想想 shell;实际上,想想好的老式 Bourne shell,因为 Bash 和亲戚有命令行编辑。键入一行输入;如果你犯了一个错误,你使用擦除字符(默认是退格,通常是;有时是DEL(来擦除前一个字符......对于非规范输入 - 想想 vi 或 vim 或 ...您按下一个字符,该程序立即可以使用该字符。在你点击回车之前,你不会被耽搁。

  4. 终端接口的描述

    本章介绍为控制异步通信端口而提供的通用终端接口。它是否支持网络连接和/或同步端口,都取决于实现。

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

如果您发现弄乱终端设置是一个太大的问题,您还可以查看跨平台的curses编程库,例如PDCurses,它将抽象底层终端类型的大多数复杂性。

关于是否

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

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