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

80

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

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

Guille
источник

Ответы:

78

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

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

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

троттердилан
источник
8
Здесь следует соблюдать осторожность, когда вы читаете файлы, которые были переданы через стандартный ввод, заключается в том, что этот и большинство приведенных ответов будут добавлять дополнительную строку в конце вашего ввода. Не все файлы заканчиваются переводом строки, но каждая итерация цикла добавляет в поток «std :: endl». В большинстве случаев это может не быть проблемой, но если проблема с целостностью файла, это может снова вас укусить.
Zoccadoum
1
Этот ответ не должен быть самым популярным. См. Ответ вики-сообщества ниже. Также, возможно, посмотрите этот вопрос: stackoverflow.com/questions/3203452/…
loxaxs
52

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

#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))
{
   ...
}

ресурс

Дегвик
источник
50

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

#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;
}
KeithB
источник
3
Ницца. Что касается «я уверен, что он использует какой-то цикл внутри» - любое решение будет.
Майкл Берр,
Кажется, что это удаляет символы новой строки из ввода, даже если они находятся прямо посередине.
Multisync
28

Изучив использование решения 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;
}
Ричард Смит
источник
7
Вероятно, это должен быть канонический ответ.
Керрек С.Б.
2
Проголосовали против. Итак, каковы плюсы и минусы по сравнению с решением KeithB? Да, вы используете std::istreambuf_iteratorвместо std::istream_iterator, но зачем?
Multisync
std :: istreambuf_iterator по умолчанию пропускает пробелы и может работать быстрее
Sopel
5

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

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

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

FrankHB
источник
вы , ребята , уже знаете, но может быть полезно некоторые из них : std::ostringstream std_input; std_input << std::cin.rdbuf(); std::string s = std_input.str(). у вас есть все входные данные s(будьте осторожны, std::stringесли входные данные слишком велики)
ribamar 01
2

Печальное примечание: я решил использовать 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 МБ ...

liborm
источник
Не нужно переписывать код на чистый C, просто добавьте std::ios::sync_with_stdio(false);перед циклом while. cin.tie(NULL);может также помочь, если вы чередуете чтение и запись.
RD
0
while(std::cin) {
 // do something
}
Брайан
источник
8
Это ужасная катастрофа для ответа, поскольку это практически гарантированно приведет к неправильному коду.
Керрек С.Б.
@KerrekSB, почему это практически гарантированно приведет к неправильному коду?
matusf 01
1
@ MatúšFerech: потому что он настоятельно предполагает, что // do somethingон не содержит дополнительных проверок возвращаемых значений операций ввода-вывода, а проверка, которую он содержит, не имеет смысла.
Kerrek SB 03
0

Подождите, я правильно вас понял? Вы используете 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();
    }
оборота убервулу
источник
0

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

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();
}
0xbadf00d
источник