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?

Guille
quelle

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().

Trotterdylan
quelle
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

Degvik
quelle
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;
}
KeithB
quelle
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;
}
Richard Smith
quelle
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.

FrankHB
quelle
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

Sie können die Funktion std :: istream :: getline () (oder vorzugsweise die Version, die mit std :: string funktioniert) verwenden, um eine ganze Zeile abzurufen . Beide haben Versionen, mit denen Sie das Trennzeichen (Zeilenendezeichen) angeben können. Der Standardwert für die Zeichenfolgenversion ist '\ n'.

Luke
quelle
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 ...

liborm
quelle
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
}
Bryan
quelle
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();
    }
uberwulu
quelle
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();
}
0xbadf00d
quelle