有没有什么方法可以调用
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()
系统调用将返回0,std::cin
会认为这是文件结束,并在eof()
上设置std::cin
。
当你想再次从标准输入读取时,只需
clear()
流的状态。
这里唯一复杂的因素是,这使得很难在
std::cin
上检测到真正的文件结束条件。当标准输入是交互式终端时问题不大;但如果标准输入可以是一个文件,这将是一个问题。
在那种情况下,你唯一现实的选择是完全放弃
std::cin
,将非阻塞模式放在文件描述符0上,poll()
或select()
它,以确定什么时候有东西要读,然后read()
它。
尽管您也可以将
poll()
或 select()
与 std::cin
一起使用,但这会变得复杂,因为您将需要明确检查是否有任何内容已缓冲在 std::cin
的 streambuf
中,因为那样会,显然,抢占任何类型的 poll()
或 select()
检查;但是通过尝试从 std::cin
中读取某些内容,您仍然冒着读取缓冲数据的风险,然后尝试从现在处于非阻塞模式的底层文件描述符中进行 read()
,这会导致伪造的结束-文件条件。
总而言之:您需要花一些额外的时间阅读和理解文件流和流缓冲区的工作原理;以及文件描述符实际如何工作,以及非阻塞模式如何工作;为了弄清楚您需要使用的正确逻辑。
哦,如果你坚持使用
std::cin
和 getline()
的非阻塞路线,你将没有简单的方法来确定 getline()
返回的字符串是否结束,因为 getline()
实际上从标准输入,或者它达到了过早的假文件结束条件并且实际上没有读取整行输入。
所以,使用非阻塞模式和
std::cin
,你将不得不使用read()
,而不是getline()
。
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上,因为它偶尔会派上用场。
我使用 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;
}
}
我试图在我自己的项目中使用@PeteBlackerThe3rd 的答案。不幸的是,我只能在之前运行
std::ios_base::sync_with_stdio(false)
时才能让它工作。所以我尝试了我自己的版本,它也是线程安全的,以防多个线程从多个源读取。
// 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;
}