select,std :: cin和std :: getline不能很好地一起玩

时间:2012-03-16 06:07:17

标签: c++ stdin

问题:

我的程序试图混合'std :: cin<< ......'和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'和'two'标记,然后遇到'stop',阻止'y'被读取。只有在提出问题时,才应阅读“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;
}

2 个答案:

答案 0 :(得分:3)

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

我观察到的另一个问题是,使用tv.tv_usec和tv.tv_sec = 0,您只需检查一次是否有可供处理的数据。如果在达到选择时没有设置输入FD,它将直接通过(结果为零)然后退出程序。我怀疑在tv中设置一些时间,让你有机会输入一些可以设置描述符的东西。我会将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

}

我会花一些时间等待,或者你只是非常快地旋转循环。选择将在有输入时返回,电视将包含剩余时间。

答案 1 :(得分:1)

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

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