checking data availability before calling std::getline

試著忘記壹切 提交于 2019-12-17 07:42:09

问题


I would like to read some data from a stream I have using std::getline. Below a sample using the std::cin.

std::string line;
std::getline( std::cin, line );

This is a blocking function i.e. if there is no data or line to read it blocks execution.

Do you know if exists a function for checking data availability before calling std::getline? I don't want to block.

How can I check whether the stream buffer is full of data valid for a successful call to std::getline?

Whatever looks like the code below

if( dataAvailableInStream() )
{
     std::string line;
     std::getline( std::cin, line );
}

回答1:


The iostream library doesn't support the concept of non-blocking I/O. I don't think there's anything in the C++ standard that does. Any good solution would likely be platform-specific. If you can use the POSIX libraries, you might look into select. It's usually used for networking stuff, but it'll work just fine if you pass it the file descriptor for stdin.




回答2:


There is no standard way to verify if getline will block. You can use:

std::cin.rdbuf()->in_avail()

to see how many characters are definitely available before a read operation may block, but you would have to read the characters one by one before re-checking in_avail as there is no way to know in advance if any of the pending characters is a newline or the actual end of the stream. A getline call might block if this wasn't the case.

Note that although if in_avail() returns a postive number there are guaranteed that at least that many characters are available before the end of the stream, the converse is not true. If in_avail() returns zero there may still be characters available and the stream might not block immediately.




回答3:


This code can help you to check existence of data in the stdin without blocking:

std::cin.seekg(0, std::cin.end);
int length = std::cin.tellg();
if (length < 0) return; //- no chars available

If stdin has some data - don't forget to set the position back to the beginning.

std::cin.seekg(0, std::cin.beg);

You can then read all data including \0 (can be more than one) at the end of the buffer:

std::vector<char> s(length);
std::cin.read(s.data(), length);

or line by line:

std::string line;
while (std::cin) {
    std::getline(std::cin, line);
    //.....
}

This code works in MSVC and gcc (Ubuntu)




回答4:


A hack could be to call kbhit() before the read. Probably not portable and fraught with danger...

#include <conio.h>
#include <iostream>

using namespace std;


char buffer[128];

if (kbhit())
{
     cin.getline(buffer, sizeof(buffer));
}



回答5:


Although nathan's peek() answer will see if there is data, there is no guarantee that std::getline() will be successful in reading a "line".

It is always much easier albeit a bit backwards to try getline and check the result of thee function call itself:

std::string line;
while( !std::getline(std::cin, line) )
{
    cout << "Enter something please" << endl;
}

This code will run until cin receives something it likes (ie can extract and place in line). I don't see peek() being necessary or useful here.

EDIT: The thing is, cin (==standard keyboard input) will have to block the program as it waits for input, how else can it get any input when it wouldn't wait?




回答6:


What problem are you trying to solve by avoiding the read blocking here?

Non-portably I imagine you could use poll or select to see if there's data to read on stdin (often fd 0 on unix systems).

Alternately you could create a second thread to do I/O and just let it block so you can continue normal processing in the main thread.




回答7:


std::iostream provides a peek function that returns the next character in the stream without removing it. So you could do something like the following (totally not tested).

bool dataAvailableInStream( std::iostream &stream )
{
  return stream.peek() != std::iostream::traits_type::eof();
}

Edit

As rubenvb points out, std::cin blocks by design. Therefore the code above will get you past getline blocking, but not cin.

Edit Edit

As Charles points out below, peek blocks if no data is available. Therefore this does not provide a complete solution. It will keep you from blocking on getline, but not blocking overall.



来源:https://stackoverflow.com/questions/3317740/checking-data-availability-before-calling-stdgetline

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!