【问题标题】:How can I use getline without blocking for input?如何在不阻塞输入的情况下使用 getline?
【发布时间】:2020-11-06 02:00:51
【问题描述】:

有什么方法可以调用getline(),如果没有输入,不阻塞等待?

我有以下代码:

 while(true){
    if(recv(sd, tBuffer, sizeof(tBuffer), MSG_PEEK | MSG_DONTWAIT) > 0) break;
    getline(cin,send);
 }

我想等待输入,但如果我在sd 套接字接收到一些数据,我想停止等待数据并退出while。我的代码现在只是停留在getline() 的第一次迭代上。我想评估getline(),如果没有可用的输入,请返回if

这可能吗?

PS:我尝试使用cin.peek(),但这也阻止了输入。

【问题讨论】:

  • 回答问题:是的,有可能。至少在 Linux 上。
  • 我正在使用 Ubuntu.. 这怎么可能?你能给我一些链接或解释给我吗?
  • 简短的回答是:1) 使用低级 I/O,而不是 getline。 2) 使用select

标签: c++ nonblocking getline


【解决方案1】:

您应该可以通过在标准输入、文件描述符 0 上设置非阻塞模式来做到这一点:

int flags = fcntl(0, F_GETFL, 0);
fcntl(0, F_SETFL, flags | O_NONBLOCK);

现在,如果没有可用的输入,底层的 read() 系统调用将返回 0,std::cin 会认为这是文件结尾,并将 eof() 设置为 std::cin

当您希望再次从标准输入中读取时,只需 clear() 流的状态即可。

这里唯一复杂的因素是,这使得在std::cin 上检测到真正的文件结束条件变得困难。当标准输入是交互式终端时,问题不大;但如果标准输入可以是文件,这将是一个问题。

在这种情况下,您唯一现实的选择是完全放弃std::cin,在文件描述符0、poll()select() 上设置非阻塞模式,以确定何时有要读取的内容,然后read()它。

虽然您也可以将poll()select()std::cin 一起使用,但这会变得很复杂,因为您需要明确检查std::cinstreambuf 中是否已经缓冲了任何内容,因为这显然会抢占任何类型的poll()select() 检查;但是通过尝试从std::cin 读取某些内容,您仍然冒着读取缓冲数据的风险,然后尝试从现在处于非阻塞模式的底层文件描述符中读取read(),这会导致假结束-文件条件。

总结一下:你需要花一些额外的时间来阅读和理解文件流和流缓冲区是如何工作的;以及文件描述符是如何工作的,以及非阻塞模式是如何工作的;为了找出你需要使用的正确逻辑。

哦,如果你坚持使用std::cingetline() 走非阻塞路线,你将无法确定getline() 返回的字符串是否结束,因为getline() 实际上读取了一个来自标准输入的换行符,或者它达到了一个过早的文件假结束条件并且没有实际读取整个输入行。

因此,使用非阻塞模式和std::cin,您将不得不使用read(),而不是getline()

【讨论】:

    【解决方案2】:

    istream::getsome() 方法可用于执行非阻塞读取。您可以使用它来构建 std::getline 的非阻塞等效项。

    bool getline_async(std::istream& is, std::string& str, char delim = '\n') {
    
        static std::string lineSoFar;
        char inChar;
        int charsRead = 0;
        bool lineRead = false;
        str = "";
    
        do {
            charsRead = is.readsome(&inChar, 1);
            if (charsRead == 1) {
                // if the delimiter is read then return the string so far
                if (inChar == delim) {
                    str = lineSoFar;
                    lineSoFar = "";
                    lineRead = true;
                } else {  // otherwise add it to the string so far
                    lineSoFar.append(1, inChar);
                }
            }
        } while (charsRead != 0 && !lineRead);
    
        return lineRead;
    }
    

    此函数的工作方式与原始 std::getline() 函数相同,只是它总是立即返回。我在my gists 上找到了它,因为它偶尔会派上用场。

    【讨论】:

    • 这仅适用于:std::ios_base::sync_with_stdio(false);
    【解决方案3】:

    我使用 select() 来检索标准输入文件描述符的状态。这适用于 Ubuntu 和嵌入式 Linux 板上。如果 stdin 仍未收到用户的 enter 击键,则 select 将等待一段时间并报告 stdin 未准备好。 stopReading 可以停止监视标准输入并继续处理其他内容。您可以根据需要对其进行编辑。它可能不适用于特殊输入 tty。

    #include <sys/select.h>
    
    static constexpr int STD_INPUT = 0;
    static constexpr __suseconds_t WAIT_BETWEEN_SELECT_US = 250000L;
    
    // Private variable in my class, but define as needed in your project
    std::atomic<bool> stopReading;
    
    ...
    
    std::string userInput = "";
    while (false == stopReading)
    {
        struct timeval tv = { 0L, WAIT_BETWEEN_SELECT_US };
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(STD_INPUT, &fds);
        int ready = select(STD_INPUT + 1, &fds, NULL, NULL, &tv);
    
        if (ready > 0)
        {
            std::getline(std::cin, userInput);
            break;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-09-01
      • 2020-01-01
      • 2020-08-06
      • 2014-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多