Подтвердить что ты не робот

Проверка доступности данных до вызова 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 );
}
4b9b3361

Ответ 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, но не блокировать в целом.