Wie man bis EOF von cin in C ++ liest


80

Ich codiere ein Programm, das Daten direkt von Benutzereingaben liest, und habe mich gefragt, wie ich (ohne Schleifen) alle Daten bis zur EOF von der Standardeingabe lesen kann. Ich habe überlegt, es zu verwenden, cin.get( input, '\0' )aber es '\0'ist nicht wirklich das EOF-Zeichen, das nur bis EOF liest oder '\0'je nachdem, was zuerst eintritt.

Oder ist die Verwendung von Schleifen der einzige Weg, dies zu tun? Wenn ja, wie geht das am besten?

Antworten:


78

Die einzige Möglichkeit, eine variable Datenmenge zu lesen, stdinist die Verwendung von Schleifen. Ich habe immer festgestellt, dass die std::getline()Funktion sehr gut funktioniert:

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

Standardmäßig wird getline()bis zu einem Zeilenumbruch gelesen. Sie können ein alternatives Abschlusszeichen angeben, aber EOF ist selbst kein Zeichen, sodass Sie nicht einfach einen Anruf tätigen können getline().


8
Wenn Sie aus Dateien lesen, die über stdin weitergeleitet wurden, sollten Sie darauf achten, dass diese und die meisten der gegebenen Antworten am Ende Ihrer Eingabe einen zusätzlichen Zeilenvorschub anhängen. Nicht alle Dateien enden mit einem Zeilenvorschub, dennoch hängt jede Iteration der Schleife "std :: endl" an den Stream an. Dies ist in den meisten Fällen möglicherweise kein Problem. Wenn jedoch die Dateiintegrität ein Problem darstellt, kann dies zu Problemen führen.
Zoccadoum

1
Dies sollte nicht die am besten bewertete Antwort sein. Siehe die unten stehende Antwort des Community-Wikis. Vielleicht sehen Sie auch diese Frage: stackoverflow.com/questions/3203452/…
loxaxs

52

Verwenden von Schleifen:

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

Ressource


50

Sie können dies ohne explizite Schleifen tun, indem Sie Stream-Iteratoren verwenden. Ich bin sicher, dass es intern eine Art Schleife verwendet.

#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
Nett. Was "Ich bin sicher, dass es intern eine Art Schleife verwendet" betrifft - jede Lösung würde dies tun.
Michael Burr

Dies scheint Zeilenumbrüche aus der Eingabe zu entfernen, selbst wenn sie genau in der Mitte auftreten.
Multisync

27

Nachdem ich die Lösung von KeithB mit untersucht hatte std::istream_iterator, entdeckte ich die std:istreambuf_iterator.

Testen Sie das Programm, um alle eingespeisten Eingaben in eine Zeichenfolge zu lesen, und schreiben Sie sie dann erneut aus:

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

int main()
{
  std::istreambuf_iterator<char> begin(std::cin), end;
  std::string s(begin, end);
  std::cout << s;
}

6
Dies sollte wahrscheinlich die kanonische Antwort sein.
Kerrek SB

2
Abgestimmt. Was sind die Vor- oder Nachteile im Vergleich zur Lösung von KeithB? Ja, Sie verwenden eine std::istreambuf_iteratoranstelle von std::istream_iterator, aber wofür?
Multisync

std :: istreambuf_iterator überspringt standardmäßig Leerzeichen und kann schneller sein
Sopel

5

Wahrscheinlich am einfachsten und allgemein effizient:

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

Verwenden Sie bei Bedarf Streams anderer Typen, z. B. std::ostringstreamals Puffer, anstelle des Standardausgabestreams.


ihr wisst es schon, aber vielleicht hilft es einigen : std::ostringstream std_input; std_input << std::cin.rdbuf(); std::string s = std_input.str(). Sie haben alle Eingaben in s(seien Sie vorsichtig, std::stringwenn die Eingabe zu groß ist)
Ribamar


2

Traurige Randnotiz: Ich habe mich für C ++ IO entschieden, um mit Boost-basiertem Code konsistent zu sein. Aus den Antworten auf diese Frage habe ich gewählt while (std::getline(std::cin, line)). Mit g ++ Version 4.5.3 (-O3) in Cygwin (Mintty) habe ich einen Durchsatz von 2 MB / s. Microsoft Visual C ++ 2010 (/ O2) hat es für denselben Code auf 40 MB / s gebracht.

Nach dem Umschreiben des E while (fgets(buf, 100, stdin))/ A auf reines C stieg der Durchsatz in beiden getesteten Compilern auf 90 MB / s. Das macht einen Unterschied für jeden Eingang, der größer als 10 MB ist ...


Sie müssen Ihren Code nicht in reines C umschreiben. Fügen Sie einfach ein std::ios::sync_with_stdio(false);vor Ihrer while-Schleife hinzu. cin.tie(NULL);kann auch helfen, wenn Sie Lesen und Schreiben verschachteln.
RD

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

8
Dies ist eine schreckliche Katastrophe einer Antwort, da sie praktisch garantiert zu falschem Code führt.
Kerrek SB

@KerrekSB warum ist es praktisch garantiert, dass es zu falschem Code führt?
Matusf

1
@ MatúšFerech: Da dies stark darauf hindeutet, dass // do somethingkeine zusätzlichen Überprüfungen der Rückgabewerte von E / A-Operationen enthalten sind und die darin enthaltene Überprüfung bedeutungslos ist.
Kerrek SB

0

Warten Sie, verstehe ich Sie richtig? Sie verwenden cin für die Tastatureingabe und möchten das Lesen von Eingaben beenden, wenn der Benutzer das EOF-Zeichen eingibt? Warum sollte der Benutzer jemals das EOF-Zeichen eingeben? Oder wollten Sie nicht mehr aus einer Datei im EOF lesen?

Wenn Sie tatsächlich versuchen, ein EOF-Zeichen mit cin zu lesen, geben Sie dann einfach das EOF als Trennzeichen an.

// Needed headers: iostream

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

Wenn Sie das Lesen aus einer Datei in der EOF beenden möchten, verwenden Sie einfach getline und geben Sie die EOF erneut als Trennzeichen an.

// 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

Eine Möglichkeit besteht darin, einen Container zu verwenden, z

std::vector<char> data;

und leiten Sie alle Eingaben in diese Sammlung um, bis sie EOFempfangen werden, d. h

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

Der verwendete Container muss jedoch möglicherweise zu oft Speicher neu zuweisen, oder Sie beenden mit einer std::bad_allocAusnahme, wenn Ihr System nicht mehr über genügend Speicher verfügt. Um diese Probleme zu lösen, können Sie eine feste Anzahl von Elementen reservierenN und diese Anzahl von Elementen isoliert verarbeiten, d. H.

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();
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.