Как читать до EOF из cin в С++

Я кодирую программу, которая считывает данные непосредственно с пользовательского ввода, и мне было интересно, как я могу (без циклов) читать все данные до EOF со стандартного ввода. Я рассматривал возможность использования cin.get( input, '\0' ), но '\0' на самом деле не является символом EOF, который просто читается до EOF или '\0', в зависимости от того, что наступит раньше.

Или использование циклов единственный способ сделать это? Если да, то как лучше всего?


person Community    schedule 14.10.2008    source источник


Ответы (10)


Единственный способ прочитать переменный объем данных из stdin — это использовать циклы. Я всегда обнаруживал, что функция std::getline() работает очень хорошо:

std::string line;
while (std::getline(std::cin, line))
{
    std::cout << line << std::endl;
}

По умолчанию getline() читается до новой строки. Вы можете указать альтернативный символ завершения, но EOF сам по себе не является символом, поэтому вы не можете просто сделать один вызов getline().

person trotterdylan    schedule 14.10.2008
comment
Здесь следует быть осторожным, когда вы читаете из файлов, которые были переданы через стандартный ввод, это то, что это и большинство приведенных ответов добавят дополнительный перевод строки в конце вашего ввода. Не все файлы заканчиваются переводом строки, но каждая итерация цикла добавляет std::endl к потоку. В большинстве случаев это может не быть проблемой, но если целостность файла является проблемой, это может вас укусить. - person Zoccadoum; 29.03.2016
comment
Это не должен быть самый популярный ответ. См. приведенный ниже ответ вики сообщества. Также, возможно, см. этот вопрос: stackoverflow.com/questions/3203452/ - person loxaxs; 16.08.2017

Использование циклов:

#include <iostream>
using namespace std;
...
// numbers
int n;
while (cin >> n)
{
   ...
}
// lines
string line;
while (getline(cin, line))
{
   ...
}
// characters
char c;
while (cin.get(c))
{
   ...
}

ресурс

person Degvik    schedule 14.10.2008

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

#include <string>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>

int main()
{
// don't skip the whitespace while reading
  std::cin >> std::noskipws;

// use stream iterators to copy the stream to a string
  std::istream_iterator<char> it(std::cin);
  std::istream_iterator<char> end;
  std::string results(it, end);

  std::cout << results;
}
person KeithB    schedule 14.10.2008
comment
Хороший. Что касается, я уверен, что он использует какой-то внутренний цикл - любое решение. - person Michael Burr; 14.10.2008
comment
Кажется, это удаляет символы новой строки из ввода, даже если они встречаются прямо посередине. - person Multisync; 07.09.2017

Изучив решение KeithB с помощью std::istream_iterator, я обнаружил std:istreambuf_iterator.

Тестовая программа для чтения всех входных данных в строку, а затем повторной записи:

#include <iostream>
#include <iterator>
#include <string>

int main()
{
  std::istreambuf_iterator<char> begin(std::cin), end;
  std::string s(begin, end);
  std::cout << s;
}
person Community    schedule 02.05.2016
comment
Вероятно, это должен быть канонический ответ. - person Kerrek SB; 19.02.2017
comment
Проголосовали против. Итак, каковы плюсы и минусы по сравнению с решением KeithB? Да, вы используете std::istreambuf_iterator вместо std::istream_iterator, но зачем? - person Multisync; 07.09.2017
comment
std::istreambuf_iterator по умолчанию пропускает пробелы и может быть быстрее - person Sopel; 19.11.2018

Вероятно, самый простой и в целом эффективный:

#include <iostream>
int main()
{
    std::cout << std::cin.rdbuf();
}

При необходимости используйте поток других типов, например std::ostringstream, в качестве буфера вместо стандартного потока вывода здесь.

person Community    schedule 18.05.2015
comment
вы, ребята, уже знаете, но может быть полезно для некоторых: std::ostringstream std_input; std_input << std::cin.rdbuf(); std::string s = std_input.str() . у вас есть все входные данные в s (будьте осторожны с std::string, если входные данные слишком велики) - person ribamar; 01.12.2015

Печальное примечание: я решил использовать C++ IO, чтобы соответствовать коду на основе повышения. Из ответов на этот вопрос я выбрал while (std::getline(std::cin, line)). Используя g++ версии 4.5.3 (-O3) в cygwin (mintty), я получил пропускную способность 2 МБ/с. Microsoft Visual C++ 2010 (/O2) увеличил скорость до 40 МБ/с для того же кода.

После перезаписи ввода-вывода на чистый C while (fgets(buf, 100, stdin)) пропускная способность подскочила до 90 МБ/с в обоих протестированных компиляторах. Это имеет значение для любого ввода размером более 10 МБ...

person Community    schedule 03.08.2012
comment
Не нужно переписывать свой код на чистый C, просто добавьте std::ios::sync_with_stdio(false); перед циклом while. cin.tie(NULL); также может помочь, если вы чередуете чтение и письмо. - person R D; 20.11.2017

Вы можете использовать std::istream::getline() ( или предпочтительнее версия, которая работает с std::string), чтобы получить всю строку . У обоих есть версии, которые позволяют указать разделитель (символ конца строки). По умолчанию для строковой версии используется '\n'.

person luke    schedule 14.10.2008

Подожди, я правильно тебя понял? Вы используете cin для ввода с клавиатуры и хотите прекратить чтение ввода, когда пользователь вводит символ EOF? Зачем пользователю когда-либо вводить символ EOF? Или вы имели в виду, что хотите прекратить чтение из файла в EOF?

Если вы на самом деле пытаетесь использовать cin для чтения символа EOF, то почему бы просто не указать EOF в качестве разделителя?

// Needed headers: iostream

char buffer[256];
cin.get( buffer, '\x1A' );

Если вы хотите прекратить чтение из файла в EOF, просто используйте getline и еще раз укажите EOF в качестве разделителя.

// Needed headers: iostream, string, and fstream

string buffer;

    ifstream fin;
    fin.open("test.txt");
    if(fin.is_open()) {
        getline(fin,buffer,'\x1A');

        fin.close();
    }
person Community    schedule 10.08.2012

Один из вариантов - использовать контейнер, например.

std::vector<char> data;

и перенаправлять все входные данные в эту коллекцию, пока не будет получено EOF, т.е.

std::copy(std::istream_iterator<char>(std::cin),
    std::istream_iterator<char>(),
    std::back_inserter(data));

Однако используемому контейнеру может потребоваться перераспределение памяти слишком часто, или вы закончите с исключением std::bad_alloc, когда вашей системе не хватит памяти. Чтобы решить эти проблемы, вы можете зарезервировать фиксированное количество N элементов и обрабатывать это количество элементов изолированно, т.е.

data.reserve(N);    
while (/*some condition is met*/)
{
    std::copy_n(std::istream_iterator<char>(std::cin),
        N,
        std::back_inserter(data));

    /* process data */

    data.clear();
}
person Community    schedule 18.12.2014

person    schedule
comment
Это ужасная катастрофа ответа, так как практически гарантированно получится неправильный код. - person Kerrek SB; 19.02.2017
comment
@KerrekSB, почему это практически гарантированно приведет к неправильному коду? - person matusf; 02.10.2018
comment
@MatúšFerech: Потому что это настоятельно предполагает, что // do something не содержит дополнительных проверок возвращаемых значений операций ввода-вывода, а проверка, которую он содержит, бессмысленна. - person Kerrek SB; 03.10.2018