Проверка доступности данных до вызова std:: getline
Я хотел бы прочитать некоторые данные из потока, который я использую std::getline
.
Ниже образца с помощью std::cin
.
std::string line;
std::getline( std::cin, line );
Это блокирующая функция, т.е. если нет данных или строк для чтения, они блокируют выполнение.
Знаете ли вы, существует ли функция проверки доступности данных перед вызовом std::getline
? Я не хочу блокировать.
Как проверить, заполнен ли буфер потока данными, действительными для успешного вызова, на std::getline
?
Как выглядит код ниже
if( dataAvailableInStream() )
{
std::string line;
std::getline( std::cin, line );
}
Ответы
Ответ 1
Библиотека iostream
не поддерживает концепцию неблокирующего ввода-вывода. Я не думаю, что что-то есть в стандарте С++. Любое хорошее решение, вероятно, будет специфичным для платформы. Если вы можете использовать библиотеки POSIX, вы можете посмотреть select
. Обычно он используется для работы в сети, но он будет работать нормально, если вы передадите ему файловый дескриптор для stdin.
Ответ 2
Нет стандартного способа проверки блокировки getline
. Вы можете использовать:
std::cin.rdbuf()->in_avail()
чтобы увидеть, сколько символов определенно доступно до того, как операция чтения может блокироваться, но вам нужно будет прочитать символы один за другим перед повторной проверкой in_avail
, поскольку нет способа узнать заранее, если какой-либо из ожидающих символы - это символ новой строки или фактический конец потока. Вызов getline
может блокироваться, если это не так.
Обратите внимание, что, хотя если in_avail()
возвращает начальное число, гарантируется, что до конца потока доступно по крайней мере много символов, обратное неверно. Если in_avail()
возвращает ноль, все равно могут быть доступны символы, и поток может не блокироваться сразу.
Ответ 3
Этот код поможет вам проверить наличие данных в stdin
без блокировки:
std::cin.seekg(0, std::cin.end);
int length = std::cin.tellg();
if (length < 0) return; //- no chars available
Если stdin
имеет некоторые данные - не забудьте установить позицию в начало.
std::cin.seekg(0, std::cin.beg);
Затем вы можете прочитать все данные, включая \0
(может быть больше одного) в конце буфера:
std::vector<char> s(length);
std::cin.read(s.data(), length);
или по строке:
std::string line;
while (std::cin) {
std::getline(std::cin, line);
//.....
}
Этот код работает в MSVC и gcc (Ubuntu)
Ответ 4
Откат может состоять в вызове kbhit() перед чтением. Вероятно, не переносимый и чреватый опасностью...
#include <conio.h>
#include <iostream>
using namespace std;
char buffer[128];
if (kbhit())
{
cin.getline(buffer, sizeof(buffer));
}
Ответ 5
Хотя ответ nathan peek()
будет видеть, есть ли данные, нет гарантии, что std::getline()
будет успешным при чтении "строки".
Это всегда намного проще, хотя немного назад, чтобы попробовать getline и проверить результат самого вызова функции:
std::string line;
while( !std::getline(std::cin, line) )
{
cout << "Enter something please" << endl;
}
Этот код будет работать до тех пор, пока cin
не получит что-то, что ему нравится (т.е. может извлечь и разместить в line
). Я не вижу здесь peek()
необходимости или полезности.
EDIT: дело в том, что cin
(== стандартный ввод с клавиатуры) придется блокировать программу, так как она ждет ввода, как еще она может получить какие-либо данные, если она не будет ждать?
Ответ 6
Какую проблему вы пытаетесь решить, избегая блокировки чтения здесь?
Неспособно предположить, что вы можете использовать poll
или select
, чтобы увидеть, есть ли данные для чтения на stdin (часто fd 0 в системах unix).
В качестве альтернативы вы можете создать второй поток для ввода-вывода и просто заблокировать его, чтобы продолжить нормальную обработку в основном потоке.
Ответ 7
std:: iostream предоставляет функцию peek, которая возвращает следующий символ в потоке, не удаляя его. Таким образом, вы можете сделать что-то вроде следующего (полностью не тестировалось).
bool dataAvailableInStream( std::iostream &stream )
{
return stream.peek() != std::iostream::traits_type::eof();
}
Edit
Как отмечает rubenvb, std::cin
блокируется по дизайну. Поэтому вышеприведенный код даст вам прошлую блокировку getline
, но не cin
.
Изменить править
Как указывает Чарльз ниже, peek
блокирует, если данные отсутствуют. Поэтому это не дает полного решения. Это не позволит вам блокировать getline
, но не блокировать в целом.