checking data availability before calling std::getline
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
i开发者_开发知识库f( dataAvailableInStream() )
{
std::string line;
std::getline( std::cin, line );
}
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.
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.
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)
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));
}
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?
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.
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.
精彩评论