如何缓冲标准::iostream
How is std::iostream buffered?
一般用例
我正在尝试实现一个基本的外壳。
描述
我需要读取用户输入,直到按下一些分隔符,以便可以执行相应的操作。这些分隔符可以是单个"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
中声明的函数和结构将终端设置为无缓冲。 基本上,您需要禁用规范输入,并将终端设置为非规范模式。 这些链接可以帮助您了解两种终端模式之间的区别:
-
非规范输入(来自 libc 手册(
在非规范输入模式下,将忽略特殊编辑字符,例如 ERASE 和 KILL。用户编辑输入的系统功能在非规范模式下被禁用,以便所有输入字符(除非它们用于信号或流量控制目的(完全按照键入的方式传递给应用程序。如果适用,由应用程序为用户提供编辑输入的方法。
规范 与非规范终端输入
对于规范输入 - 想想 shell;实际上,想想好的老式 Bourne shell,因为 Bash 和亲戚有命令行编辑。键入一行输入;如果你犯了一个错误,你使用擦除字符(默认是退格,通常是;有时是DEL(来擦除前一个字符......对于非规范输入 - 想想 vi 或 vim 或 ...您按下一个字符,该程序立即可以使用该字符。在你点击回车之前,你不会被耽搁。
终端接口的描述
本章介绍为控制异步通信端口而提供的通用终端接口。它是否支持网络连接和/或同步端口,都取决于实现。
但从本质上讲,您遇到的问题不在于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 系统上实现这一点。
- 使用CMake检测支持的C++标准
- 如何理解C++标准N3337中的expr.const.cast子句8
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 编译标准库类型
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 是否可以用"iostream"包装现有的TCP/OOpenSSL会话
- 需要从 istream 和 ostream 派生 iostream
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 铸造标准::有没有回到原来的类型
- 标准 N3337 5.2.10 第 7 条中的C++"类型"是什么意思?
- this_thread::sleep_for和计时时钟之间的关系是否由C++11标准指定
- std::带有自定义缓冲区的 iostream 不允许我写入
- 标准库类型的赋值运算符的引用限定符
- 标准是否严格定义了该程序应该如何编译?
- 如何从Windows应用程序输出到标准?
- 编译 PyGAMMA Mac Lion:g++ 找不到标准的 C++ 头文件(字符串/iostream..)
- 如何缓冲标准::iostream
- 格式化的输出运算符标准iostream对象可以抛出什么样的异常
- 是否可以轮询标准C++iostream操纵器的状态
- <iostream> 标准:函数内的cout不打印(修复:用于循环问题)