c++ 如何使用getline而不阻塞输入?

9rbhqvlz  于 2023-05-02  发布在  其他
关注(0)|答案(4)|浏览(177)

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

y3bcpkx1

y3bcpkx11#

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

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

现在,如果没有可用的输入,底层的read()系统调用将返回0,std::cin将认为这是文件的结尾,并在std::cin上设置eof()
当你想再次从标准输入中读取数据时,只需要clear()流的状态。
这里唯一的复杂因素是,这使得在std::cin上检测真实的的文件结束条件变得困难。当标准输入是一个交互式终端时,这不是什么大问题;但如果标准输入可以是一个文件,这将是一个问题。
在这种情况下,您唯一现实的选择是完全放弃std::cin,在文件描述符0,poll()select() it上设置非阻塞模式,以确定何时有内容要读取,然后read()它。
虽然您也可以将poll()select()std::cin一起使用,但这将变得复杂,因为您需要显式地检查std::cinstreambuf中是否已经缓冲了任何内容,因为这显然会抢占任何类型的poll()select()检查;但是如果试图从std::cin中读取一些内容,您仍然会面临阅读缓冲数据的风险,然后尝试从现在处于非阻塞模式的底层文件描述符中读取read(),这会导致虚假的文件结束条件。
总结如下:你需要投入一些额外的时间来阅读和理解文件流和流缓冲区是如何工作的;以及文件描述符实际上如何工作,以及非阻塞模式如何工作;为了找出正确的逻辑,你需要使用。
哦,如果你坚持使用std::cingetline()的非阻塞路由,你将没有简单的方法来确定getline()返回的字符串是否结束,因为getline()实际上从标准输入中读取了一个换行符,或者它达到了一个过早的文件伪结束条件,而不是整个输入行实际上已经被读取。
因此,使用非阻塞模式和std::cin,您将被迫使用read(),而不是getline()

dwbf0jvd

dwbf0jvd2#

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上,因为它偶尔会派上用场。

v2g6jxz6

v2g6jxz63#

我使用select()检索stdin文件描述符的状态。这在Ubuntu和嵌入式Linux板上工作。如果stdin仍然没有收到来自用户的回车键,select将等待一段时间,并报告stdin没有准备好。stopReading可以停止监视stdin并继续其他东西。您可以根据需要对其进行编辑。它可能不适用于特殊的输入tty's。

#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;
    }
}
7cwmlq89

7cwmlq894#

我试图在我自己的项目中使用@PeteBlackerThe3rd的答案。不幸的是,我只能在运行std::ios_base::sync_with_stdio(false) prior时才能让它工作。所以我尝试了我自己的版本,它也是线程安全的,以防多个线程从多个源阅读。

// Returns 1 on success, 0 when not done, and -1 on failure (check errno)
// str is initially expected to be an empty string and should only altered by this function.
int getline_async_thread_safe(const int& fd, std::string& str, char delim = '\n') {
    int chars_read;
    do {
        char buf[2] = { 0 };
        chars_read = (int) read(fd, buf, 1);
        if (chars_read == 1) {
            if (*buf == delim) {
                return 1;
            }
            str.append(buf);
        } else {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                chars_read = 0;
                break;
            }
        }
    } while (chars_read > 0);

    return chars_read;
}

调用的函数示例:

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

std::string output;
int retval;
while(some_condition) {
    retval = getline_async_thread_safe(0, output);
    if (retval == 0) {
        // Process std::string output 
        // Make sure to reset string if continuing through loop
    } else if (retval == -1) {
        // Check errno
    }
    // Do other things while waiting for the newline.
}
if (retval < 0) {
    // Handle error with errno.
} else {
    std::cout << "Output: " << output << std::endl;
}

相关问题