我正在尝试实现一个基本的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;
}
}
但是,循环似乎只有在按下“new-line”键后才会执行。此行为会终止用户输入的交互式本质。
我知道std :: ostream是缓冲的,所以在发生某些事件之前内容不会写入磁盘,但是std :: istream呢。它是缓冲的吗?如果是的话,它是如何绕过这种行为我有什么选择?
此外,我已将此问题标记为“家庭作业”,因为如果不是学校的活动,这是我自己尝试做的练习,我不想只挑选一个实施所有的工作这个东西。
答案 0 :(得分:7)
如果您使用的是POSIX操作系统,则可以使用termios.h
中声明的功能和结构将终端设置为无缓冲。基本上你需要禁用规范输入,并为非规范模式设置终端。这些链接可以帮助您理解两种终端模式之间的区别:
Noncanonical Input(来自libc手册)
在非规范输入模式下,将忽略ERASE和KILL等特殊编辑字符。用户编辑输入的系统工具在非规范模式下被禁用,因此所有输入字符(除非它们特别用于信号或流控制目的)都将完全按照键入的方式传递给应用程序。应用程序可以根据需要为用户提供编辑输入的方法。
Canonical vs. non-canonical terminal input
对于规范输入 - 想想shell;实际上,想想老式的Bourne shell,因为Bash和亲戚都有命令行编辑。你键入一行输入;如果你犯了一个错误,你使用擦除字符(默认是退格,通常;有时DEL)擦除前一个字符...对于非规范输入 - 想想vi或vim或...你按一个字符,它立即可用于该程序。在你回来之前,你不会被阻止。
Description of Terminal Interface
本章介绍了为控制异步通信端口而提供的通用终端接口。无论是支持网络连接还是同步端口,它都依赖于实现。
从本质上讲,您遇到的问题不在于C ++ iostream接口本身,而在于如何设置C ++ iostream接口正在读取的控制终端。因此,利用无缓冲的I / O将是一个依赖于平台的操作,并且将根据您使用的是Windows还是实际的POSIX兼容平台(这包括适用于Windows的POSIX环境,如Cygwin)而有所不同。
如果您发现使用终端设置搞乱是一个太大的问题,您还可以查看一个跨平台的curses programming library,例如PDCurses,它将抽象出大多数复杂性基础终端类型。
答案 1 :(得分:4)
关于是否以及如何缓冲std::istream
的直接问题的答案是:是的,std::istream
是使用从std::streambuf
派生的类的缓冲区,它定义了实际的缓冲和读取方法具体来源(或者,当使用std::ostream
作为目的地时)。这是否真的有任何缓冲取决于这个具体的类,它的操作通常无法避免。
0
和tcgetattr()
将标准输入流(使用文件描述符tcsetattr()
)转换为非规范模式。我不知道如何在非POSIX系统上实现这一点。