проверка доступности данных перед вызовом std :: getline - PullRequest
26 голосов
/ 23 июля 2010

Я хотел бы прочитать некоторые данные из потока, который использую 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 );
}

Ответы [ 7 ]

17 голосов
/ 23 июля 2010

Не существует стандартного способа проверить, будет ли getline блокироваться.Вы можете использовать:

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

, чтобы увидеть, сколько символов определенно доступно, прежде чем операция чтения может заблокироваться, но вам придется прочитать символы по одному перед повторной проверкой in_avail, так как нетспособ заранее узнать, является ли какой-либо из ожидающих символов новой строкой или фактическим концом потока.Вызов getline может блокироваться, если это не так.

Обратите внимание, что, хотя in_avail() возвращает положительное число, гарантируется, что по крайней мере столько символов будет доступно до конца потока,обратное неверно.Если in_avail() возвращает ноль, все еще могут быть доступны символы, и поток может не блокироваться немедленно.

8 голосов
/ 23 июля 2010

Библиотека iostream не поддерживает концепцию неблокирующего ввода / вывода. Я не думаю, что есть что-то в стандарте C ++, что делает. Любое хорошее решение, скорее всего, будет зависеть от платформы. Если вы можете использовать библиотеки POSIX, вы можете заглянуть в select. Обычно он используется для сетевого взаимодействия, но он будет работать нормально, если вы передадите ему дескриптор файла для stdin.

4 голосов
/ 05 мая 2017

Этот код может помочь вам проверить наличие данных в 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)

1 голос
/ 19 декабря 2011

Может быть взломать вызов kbhit () перед чтением.Вероятно, не переносимый и чреватый опасностью ...

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

using namespace std;


char buffer[128];

if (kbhit())
{
     cin.getline(buffer, sizeof(buffer));
}
0 голосов
/ 23 июля 2010

Какую проблему вы пытаетесь решить, избегая здесь блокировки чтения?

Непередаваемо. Я полагаю, вы могли бы использовать poll или select, чтобы посмотреть, есть ли данные для чтения на stdin (часто это0 в системах unix).

В качестве альтернативы вы можете создать второй поток для ввода-вывода и просто заблокировать его, чтобы продолжить нормальную обработку в основном потоке.

0 голосов
/ 23 июля 2010

Хотя ответ Натана peek() покажет, есть ли данные, нет гарантии, что std::getline() удастся прочитать "строку".

Всегда намного проще, хотя и немного задом наперед, попробовать getline и проверить результат самого вызова функции:

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

Этот код будет работать до тех пор, пока cin не получит что-то, что ему нравится (т.е. может извлечь и поместить в line). Я не вижу peek() необходимым или полезным здесь.

РЕДАКТИРОВАТЬ: Дело в том, что cin (== стандартный ввод с клавиатуры) придется блокировать программу, пока она ожидает ввода, как еще она может получить любой ввод, когда она не будет ждать?

0 голосов
/ 23 июля 2010

std :: iostream предоставляет функцию просмотра, которая возвращает следующий символ в потоке, не удаляя его. Таким образом, вы можете сделать что-то вроде следующего (полностью не проверено).

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

Редактировать

Как указывает rubenvb, std::cin блокирует дизайн. Поэтому приведенный выше код поможет вам преодолеть блокировку getline, но не cin.

Редактировать Редактировать

Как указывает Чарльз ниже, peek блокируется, если данные недоступны. Поэтому это не дает полного решения. Он не позволит вам блокировать getline, но не блокирует в целом.

...