如何在不阻塞输入的情况下使用 getline?

How can I use getline without blocking for input?

有没有什么方法可以调用 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(),但它也会阻止输入。

您应该可以通过在标准输入、文件描述符 0:

上设置非阻塞模式来做到这一点
int flags = fcntl(0, F_GETFL, 0);
fcntl(0, F_SETFL, flags | O_NONBLOCK);

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

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

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

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

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

总而言之:您需要投入一些额外的时间来阅读和理解文件流和流缓冲区的工作原理;以及文件描述符实际如何工作,以及非阻塞模式如何工作;为了弄清楚您需要使用的正确逻辑。

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

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

istream::getsome() method can be used to perform non-blocking reads. You can use it to build a non-blocking equivalent of 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() 函数的工作方式相同,只是它总是立即 returns。我在 my gists 上找到了它,因为它偶尔会派上用场。

我使用 select() 来检索标准输入文件描述符的状态。这适用于 Ubuntu 和嵌入式 Linux 板。如果 stdin 仍未收到用户的回车键,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;
    }
}