Jak czytać do EOF z cin w C ++

80

Koduję program, który czyta dane bezpośrednio z danych wejściowych użytkownika i zastanawiałem się, jak mogę (bez pętli) odczytać wszystkie dane do EOF ze standardowego wejścia. Rozważałem użycie, cin.get( input, '\0' )ale '\0'tak naprawdę nie jest to znak EOF, który czyta tylko do EOF lub '\0', cokolwiek nastąpi wcześniej.

A może używanie pętli jest jedynym sposobem na to? Jeśli tak, to jaki jest najlepszy sposób?

Guille
źródło

Odpowiedzi:

78

Jedynym sposobem, w jaki możesz odczytać zmienną ilość danych, stdinjest użycie pętli. Zawsze uważałem, że std::getline()funkcja działa bardzo dobrze:

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

Domyślnie getline()czyta aż do nowej linii. Możesz określić alternatywny znak zakończenia, ale EOF sam w sobie nie jest znakiem, więc nie możesz po prostu wykonać jednego wywołania getline().

trotterdylan
źródło
8
Coś, na co należy uważać, gdy czytasz z plików, które zostały przesłane przez stdin, polega na tym, że to i większość udzielonych odpowiedzi będzie dołączać dodatkowy kanał na końcu twojego wejścia. Nie wszystkie pliki kończą się znakiem nowej linii, ale każda iteracja pętli dołącza do strumienia „std :: endl”. W większości przypadków może to nie stanowić problemu, ale jeśli problemem jest integralność plików, może to powrócić, aby Cię ugryźć.
Zoccadoum
1
To nie powinna być najbardziej pozytywna odpowiedź. Zobacz poniższą odpowiedź wiki społeczności. Może też zobacz to pytanie: stackoverflow.com/questions/3203452/…
loxaxs
52

Korzystanie z pętli:

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

ratunek

Degvik
źródło
50

Możesz to zrobić bez jawnych pętli, używając iteratorów strumienia. Jestem pewien, że wewnętrznie używa jakiejś pętli.

#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
źródło
3
Miły. Jeśli chodzi o „Jestem pewien, że używa wewnętrznie jakiejś pętli” - każde rozwiązanie tak.
Michael Burr,
Wydaje się, że usuwa to znaki nowego wiersza z wejścia, nawet jeśli występują one w środku.
Multisync
28

Po zbadaniu rozwiązania KeithB przy użyciu std::istream_iterator, odkryłem std:istreambuf_iterator.

Przetestuj program, aby wczytywał wszystkie dane wejściowe potokiem do łańcucha, a następnie wypisz go ponownie:

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

int main()
{
  std::istreambuf_iterator<char> begin(std::cin), end;
  std::string s(begin, end);
  std::cout << s;
}
Richard Smith
źródło
7
To prawdopodobnie powinna być kanoniczna odpowiedź.
Kerrek SB
2
Głosowano w dół. Jakie są zatem zalety i wady w porównaniu z rozwiązaniem KeithB? Tak, używasz std::istreambuf_iteratorzamiast an std::istream_iterator, ale po co?
Multisync
std :: istreambuf_iterator domyślnie pomija białe znaki i może być szybsze
Sopel
5

Prawdopodobnie najprostszy i ogólnie skuteczny:

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

W razie potrzeby użyj tutaj strumienia innych typów, takich std::ostringstreamjak bufor zamiast standardowego strumienia wyjściowego.

FrankHB
źródło
wy już wiecie, ale mogą być pomocne dla niektórych: std::ostringstream std_input; std_input << std::cin.rdbuf(); std::string s = std_input.str(). masz wszystkie dane wejściowe s(uważaj, std::stringjeśli dane wejściowe są zbyt duże)
Ribamar
2

Smutna uwaga: zdecydowałem się użyć C ++ IO, aby być spójnym z kodem opartym na boost. Z odpowiedzi na to pytanie wybrałemwhile (std::getline(std::cin, line)) . Używając g ++ w wersji 4.5.3 (-O3) w Cygwin (Mintty) uzyskałem przepustowość 2 MB / s. Microsoft Visual C ++ 2010 (/ O2) zrobił 40 MB / s dla tego samego kodu.

Po przepisaniu IO do czystego C while (fgets(buf, 100, stdin))przepustowość wzrosła do 90 MB / sw obu testowanych kompilatorach. To robi różnicę w przypadku każdego wejścia większego niż 10 MB ...

liborm
źródło
Nie ma potrzeby przepisywania kodu do czystego C, po prostu dodaj std::ios::sync_with_stdio(false);przed pętlą while. cin.tie(NULL);może również pomóc, jeśli przeplatasz czytanie i pisanie.
RD
0
while(std::cin) {
 // do something
}
Bryan
źródło
8
To straszna katastrofa odpowiedzi, ponieważ praktycznie gwarantuje, że spowoduje to błędny kod.
Kerrek SB
@KerrekSB dlaczego praktycznie gwarantuje się, że spowoduje to niewłaściwy kod?
matusf
1
@ MatúšFerech: Ponieważ zdecydowanie sugeruje, że // do somethingnie zawiera dodatkowych kontroli zwracanych wartości operacji we / wy, a sprawdzenie, które zawiera, jest bez znaczenia.
Kerrek SB
0

Czekaj, czy dobrze cię rozumiem? Używasz cin do wprowadzania danych z klawiatury i chcesz przestać czytać dane wejściowe, gdy użytkownik wprowadzi znak EOF? Dlaczego użytkownik miałby kiedykolwiek wpisywać znak EOF? A może chodziło Ci o to, że chcesz przestać czytać z pliku w EOF?

Jeśli faktycznie próbujesz użyć funkcji cin do odczytania znaku EOF, to dlaczego nie określić po prostu EOF jako separatora?

// Needed headers: iostream

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

Jeśli zamierzasz przestać czytać z pliku w EOF, po prostu użyj getline i jeszcze raz określ EOF jako separator.

// Needed headers: iostream, string, and fstream

string buffer;

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

        fin.close();
    }
uberwulu
źródło
0

Jedną z opcji jest użycie pojemnika, np

std::vector<char> data;

i przekieruj wszystkie dane wejściowe do tej kolekcji, dopóki nie EOFzostaną odebrane, tj

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

Jednak używany kontener może wymagać zbyt częstego ponownego przydzielania pamięci lub zakończy się std::bad_allocwyjątkiem, gdy w systemie zabraknie pamięci. Aby rozwiązać te problemy, można zarezerwować określoną liczbę Nelementów i przetworzyć te ilości oddzielnie, tj

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
źródło