select,std::cin和std::getline在一起打得不好



问题:

我的程序正在尝试混合"std::cin<lt;…'和std::getline,并带有短划线"select"。它有一些奇怪的行为,但不是典型的"你需要std::cin.ignore"情况。

所需操作:

其目的是从std::cin读取令牌(如果有的话),直到遇到令牌"stop"。std::cin中剩下的内容稍后将作为对一个问题的回答进行阅读。例如:

$ echo 'one two stop y' | ./a.out
read 'one'
read 'two'
Question? y
read 'y'
$

读取"one"one_answers"two"标记,然后遇到阻止读取"y"的"stop"。只有在提出问题时,才应阅读"y"。

实际操作:

read 'one two stop y'
Question? read ''
Question? read ''
Question? read ''
...

该程序首先调用"select",以检查是否有令牌在等待,然后使用std::cin,然后是std::getline。通常,您可能会认为我需要读取std::getline未读取的挂起的'\n'。但是select并没有表示有任何输入在等待,所以std::cin错过了它,std::getline得到了所有的输入

然而,如果添加了超时,它可以完美地工作:

  tv.tv_usec = 1000;

最后,一个问题:

发生了什么事?我犯了什么愚蠢的错误?

代码:

#include <iostream>
#include <sys/select.h>
int main (int argc, char* argv[])
{
  // Step 1, read until 'stop'.
  struct timeval tv;
  tv.tv_sec = 0;
  tv.tv_usec = 0;
  fd_set fds;
  FD_ZERO (&fds);
  FD_SET (STDIN_FILENO, &fds);
  int result = select (STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
  if (result && result != -1)
  {
    if (FD_ISSET (0, &fds))
    {
      std::string token;
      while (std::cin >> token)
      {
        if (token == "stop")
          break;
        std::cout << "read '" << token << "'n";
      }
    }
  }
  // Step 2, ask a question.
  std::string answer;
  do
  {
    std::cout << "Question? ";
    std::getline (std::cin, answer);
    std::cout << "read '" << answer << "'n";
  }
  while (answer != "y");
  return 0;
}

如果你有tv.tv_usec=0,那么它只是在轮询,看看是否有什么东西。文件语句必须存在某些内容才能返回0以外的内容(这表示在没有可用输入的情况下达到时间)。

我观察到的另一个问题是,在tv.tv_usec和tv.tv_sec=0的情况下,您只检查一次,看看是否有可用的数据要处理。如果在达到选择时未设置输入FD,则它将通过(结果为零),然后退出程序。我怀疑在电视上设置一些时间,可以让你有机会键入一些可以设置描述符的内容。我会把select放在一个循环中,等待输入发生。按照当前编写程序的方式,select只会检查一次,看看是否有输入。下面我有一个循环的例子。

while ( 1 ) 
{
    FD_ZERO (&fds);   
    FD_SET (STDIN_FILENO, &fds);
    tv.tv_sec = 0; 
    tv.tv_usec = 100;
    result = select (STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
    // Code here to read in line if result is not zero
}

我也会花一些时间等待,否则你会很快在循环中旋转。当有输入时,选择将返回,电视将包含剩余时间。

混合cin和cin.getline的大多数问题都是冲洗问题的结果或副作用。我认为在这种情况下也不会有什么不同。

编辑:是的,你必须使用peek来检查一个字符,而不是从流中提取它。

最新更新