如何获取`cin`以读取原始模式终端

问题描述

为什么标准流可以在规范模式下接收来自终端的输入,但是您将其置于原始模式下却突然不再有效?我很了解POSIX串行编程,通常您使用read。我试图更好地理解标准流。

#include <termios.h>
#include <unistd.h>
#include <iostream>

termios original;

void enableRawMode() {
  tcgetattr(STDIN_FILENO,&original);
  termios raw = original;
  cfmakeraw(&raw);
  raw.c_cc[VMIN] = 0;
  raw.c_cc[VTIME] = 1;
  tcsetattr(STDIN_FILENO,TCSAFLUSH,&raw);
}

int main() {
  enableRawMode();

  char c;

  // This works as expected.
  //int nread;
  //while ((nread = read(STDIN_FILENO,&c,1)) != 1) {
  //  if (nread == -1 && errno != EAGAIN) {
  //    break;
  //  }
  //}

  // This loops forever,the failbit is always true,gcount is always 0.
  while (!(std::cin.get(c))) {
    if (std::cin.bad() || std::cin.eof()) {
      break;
    }

    std::cin.clear();
  }

  tcsetattr(STDIN_FILENO,&original);
}

解决方法

经过大量讨论和一些调查,我发现这对给定标准流的实现是一个限制。基本上,我推断出标准流在您的终端处于煮熟模式时可以正常工作,但在原始模式下则不可靠。

可以将代码简化为使用std::cin.rdbuf()->sbumpc(),在这种情况下将调用uflow,后者很可能会调用underflow,这将从底层设备中获取数据。与标准流关联的流缓冲区是实现定义的。

sbumpc的第一次调用,更具体地说是对underflow的最终调用,将在从标准输入中读取时启动与VTIME关联的计时器。如果在超时时间内收到输入,cin将按预期工作。但是,如果发生超时,则流缓冲区将进入未定义状态,并且sbumpc将永远返回EOF

状态与流而不是流缓冲区相关联,因此调用std::cin.clear()会清除状态标志,但不能纠正流缓冲区中仍然存在的潜在问题。

在重置搜索位置时可能有一种解决方案,但是同样,给定的标准流是实现定义的,并且不知道它们可能拥有什么内部状态,或者在一个平台上是否成功,是否会在另一个平台上成功。据我所知,该文档只是不存在的。

两种可能的解决方案是直接使用POSIX API并喜欢它,或者编写使用POSIX API的自己的流实现。