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


80

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

Або використання циклів - єдиний спосіб це зробити? Якщо так, то який найкращий спосіб?

Відповіді:


78

Єдиний спосіб читання змінної кількості даних stdin- це використання циклів. Я завжди виявляв, що std::getline()функція працює дуже добре:

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

За замовчуванням getline()читає до нового рядка. Ви можете вказати альтернативний символ завершення, але EOF сам по собі не є символом, тому ви не можете просто зробити один дзвінок getline().


8
Тут слід бути обережним, коли ви читаєте з файлів, які були передані через stdin, - це те, що ця та більшість поданих відповідей додадуть додатковий рядок у кінці вашого введення. Не всі файли закінчуються подачею рядків, але кожна ітерація циклу додає "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;
}

3
Приємно. Що стосується "Я впевнений, що він використовує якийсь цикл внутрішньо" - будь-яке рішення буде.
Michael Burr

Здається, це видаляє символи нового рядка із введення, навіть якщо вони трапляються прямо посередині.
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
Це, мабуть, має бути канонічною відповіддю.
Kerrek SB

2
Проголосовано проти. То які плюси чи мінуси в порівнянні з рішенням KeithB? Так, ви використовуєте std::istreambuf_iteratorзамість std::istream_iterator, а для чого?
Multisync

std :: istreambuf_iterator пропускає пробіли за замовчуванням і може бути швидшим
Сопель

5

Ймовірно найпростіший і загалом ефективний:

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

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


ви , хлопці , вже знаєте, але може бути корисно деякі з них : std::ostringstream std_input; std_input << std::cin.rdbuf(); std::string s = std_input.str(). у вас є всі входи s(будьте обережні, std::stringякщо вхід занадто великий)
ribamar

2

Ви можете використовувати функцію std :: istream :: getline () (або бажано версію, яка працює з функцією std :: string ), щоб отримати цілий рядок. Обидва мають версії, які дозволяють вказати роздільник (символ кінця рядка). За замовчуванням для рядкової версії використовується "\ n".


2

Сумна побічна примітка: я вирішив використовувати C ++ IO, щоб відповідати коду на основі boost. З відповідей на це питання я вибрав while (std::getline(std::cin, line)). Використовуючи g ++ версії 4.5.3 (-O3) у cygwin (mintty), я отримав пропускну здатність 2 МБ / с. Microsoft Visual C ++ 2010 (/ O2) зробив 40 МБ / с для того самого коду.

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


Не потрібно переписувати код на чистий C, просто додайте a std::ios::sync_with_stdio(false);перед циклом while. cin.tie(NULL);також може допомогти, якщо ви чергуєте читання та письмо.
РД

0
while(std::cin) {
 // do something
}

8
Це жахлива катастрофа відповіді, оскільки вона практично гарантовано призведе до неправильного коду.
Kerrek SB

@KerrekSB чому практично гарантовано призводить до помилки коду?
matusf

1
@ MatúšFerech: Оскільки він настійно рекомендує // do somethingне містити додаткових перевірок повернутих значень операцій вводу-виводу, а перевірка, яку він містить, безглузда.
Керрек СБ

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();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.