2014-10-03 10 views
5

Ich bin ein Informatikstudent, ein so wenig Erfahrung mit der C++ - Sprache (wenn es mein erstes Semester mit dieser Sprache ist) oder Codierung für diese Angelegenheit.Wie genau funktioniert der Extrakt >> Operator in C++

ich gegeben wurde, einen Auftrag ganze Zahlen aus einer Textdatei in der einfachen Form zu lesen:

19 3 -2 9 14 4 
5 -9 -10 3 
. 
. 
. 

Dieser schickte mich auf einer Reise I/O-Betreiber besser zu verstehen, da ich bin verpflichtet, zu bestimmte dinge mit diesem stream (duh.)

Ich habe überall gesucht und konnte keine einfache Erklärung finden, wie der Extrakt >> operator intern funktioniert. Lassen Sie mich meine Frage klären:

Ich weiß, dass der Extraktor >> Operator ein fortfahren Element extrahiert, bis es Raum, Tab oder Newline trifft. Ich versuche herauszufinden, wo der Zeiger (?) Oder der Leseort (?) NACH dem Extrahieren eines Elements sein würde. Wird es auf dem letzten Char des Elements gerade entfernt oder wurde es entfernt und damit weg? Wird es auf dem Leerzeichen/Tab/'\ n' Zeichen selbst sein? Vielleicht der Anfang des nächsten zu extrahierenden Elements?

Ich hoffe, ich war klar genug. Mir fehlt der passende Jargon, um mein Problem klarer zu beschreiben.


Hier ist, warum ich das wissen müssen: (falls jemand fragt ...) Eine der Anforderungen ist es, alle Zahlen in jeder Zeile getrennt zu summieren. Ich habe eine Schleife erstellt, um alle Ganzzahlen eins nach dem anderen zu extrahieren, bis sie das Ende der Datei erreicht. Ich habe jedoch bald gelernt, dass der Operator extract >> Leerzeichen/Tab/Newline ignoriert. Was ich versuchen möchte ist, ein Element zu extrahieren und dann mit inputFile.get() das Leerzeichen/Tab/Newline zu erhalten. Wenn es ein Newline ist, dann tu was ich tun soll. Dies funktioniert nur, wenn der Streamzeiger in einer guten Position ist, um das Leerzeichen/Tab/Newline nach der letzten Extraktion >> zu extrahieren.


In my previous question, habe ich versucht, es zu lösen getline() verwendet und eine SSTRING.


LÖSUNG:

Aus Gründen meine konkrete Frage zu beantworten, wie Operator >> funktioniert, ich hatte Ben Voigt Antwort als die beste zu akzeptieren. Ich habe die anderen hier vorgeschlagenen Lösungen verwendet (mit einem sstring für jede Zeile) und sie haben funktioniert! (Sie können es in meiner vorherigen Frage der Link sehen) Allerdings habe ich eine andere Lösung mit Ben Antwort implementiert und es funktionierte auch:

 . 
     . 
     . 

if(readFile.is_open()) { 
     while (readFile >> newInput) { 
       char isNewLine = readFile.get(); //get() the next char after extraction 

       if(isNewLine == '\n')    //This is just a test! 
         cout << isNewLine;   //If it's a newline, feed a newline. 
       else 
         cout << "X" << isNewLine; //Else, show X & feed a space or tab 

       lineSum += newInput; 
       allSum += newInput; 
       intCounter++; 
       minInt = min(minInt, newInput); 
       maxInt = max(maxInt, newInput); 

       if(isNewLine == '\n') { 
         lineCounter++; 
         statFile << "The sum of line " << lineCounter 
         << " is: " << lineSum << endl; 
          lineSum = 0; 
       } 
     } 
     . 
     . 
     . 

Ohne Bezug auf meine numerischen Werte, die Form ist richtig! Beide Räume und ‚\ n die wurden gefangen: test

Danke Ben Voigt :)

Dennoch ist diese Lösung sehr Format abhängig und ist sehr zerbrechlich. Wenn eine der Zeilen etwas anderes vor '\ n' hat (wie Leerzeichen oder Tabulatorzeichen), wird der Zeilenumbruch vom Code nicht erkannt. Daher ist die andere Lösung, die getline() und sstrings verwendet, viel zuverlässiger.

+0

scheint, dass Sie ganze Zeilen lesen möchten, und dann sie selbst analysieren. – Deduplicator

+0

Sie fragen meistens nach irrelevanten Implementierungsdetails. 'operator >>' extrahiert einen Wert und bringt den Stream weiter. Wenn Sie ein komplizierteres Parsing wünschen, verwenden Sie einen echten Parser. –

+1

Hast du versucht, 'get' zu nennen und zu prüfen, welchen Charakter du bekommst? –

Antwort

3

Nach der Extraktion wird der Stream-Zeiger auf den Leerraum platziert, der die Extraktion beendet hat (oder ein anderes ungültiges Zeichen, in welchem ​​Fall auch das Failbit gesetzt wird).

Dies ist jedoch nicht wirklich wichtig, da Sie nicht für das Überspringen dieses Whitespace verantwortlich sind. Die nächste Extraktion ignoriert Leerzeichen, bis gültige Daten gefunden werden.

Zusammengefasst:

  • führende Leerzeichen
  • nachfolgende Leerzeichen ignoriert wird, wird in dem Strom links

Es gibt auch die noskipws Modifikator, der verwendet werden kann, um das Standardverhalten zu ändern.

1

Gemäß cppreference.com delegiert der Standard operator>> die Arbeit an std::num_get::get. Dies erfordert einen Eingabe-Iterator. Eine der Eigenschaften eines Eingabe-Iterators besteht darin, dass Sie ihn mehrfach dereferenzieren können, ohne ihn zu erhöhen. Wenn also ein nicht numerisches Zeichen erkannt wird, wird der Iterator auf dieses Zeichen zeigen.

+0

cppreference.com ist ein guter Ort, um jemanden für weitere Lektüre zu verweisen, aber es als Grundlage für eine Antwort zu zitieren scheint falsch - es ist keine Autorität. Ihre Antwort sollte sachlich begründet sein in dem, was der Standard sagt, ganz abgesehen davon, dass Sie es angemessen finden, aus dem Standard in Ihrer Antwort zu zitieren. –

+0

Das ist in Ordnung, aber es sagt Ihnen nicht, wo der Lesezeiger im Eingabestrom landen wird. (Beachten Sie, dass 'std :: istream_iterator' und' std :: istreambuf_iterator' in dieser Hinsicht unterschiedliche Verhaltensweisen haben.) –

+0

@BenVoigt Sie können Ihre eigene Antwort erstellen. Ich habe fast nie das Bedürfnis, zu diesem Extrem zu gehen, was bedeutet, dass ich nicht genug geübt bin, um gut darin zu sein. –

2

Die operator>> übergibt die aktuelle Position in der Datei ein Zeichen über das letzte extrahierte Zeichen hinaus (das an Ende der Datei sein kann). Das hilft nicht unbedingt bei Ihrem Problem; Nach dem letzten Wert in einer Zeile können Leerzeichen oder Tabulatoren stehen. Sie könnte überspringen vorwärts jedes Zeichen zu lesen und zu prüfen, ob es ein weißer Raum anders als '\n' ist, aber ein weitaus idiomatische Lesart Linie orientiert Eingang std::getline zu lesen die Linie zu verwenden ist, initialisiert dann eine std::istringstream zu Extrakt die ganzen Zahlen aus der Leitung:

std::string line; 
while (std::getline(source, line)) { 
    std::istringstream values(line); 
    // ... 
} 

Damit ist auch sichergestellt, dass in der Linie bei einem Formatfehler, der Fehlerzustand des Haupteingangs ist nicht betroffen, und Sie können mit der nächsten Zeile fortzusetzen.

+0

Vielen Dank für Ihre Antwort! Ich habe es tatsächlich versucht, aber wahrscheinlich habe ich es irgendwie vermasselt [in meiner vorherigen Frage] (http://stackoverflow.com/questions/26134028/not-getting-all-lines-from-a-text-file-when-using-using-used- getlin-in-c). Sie können meinen klobigen Code dort finden. Es nimmt die erste Zeile auf und arbeitet korrekt weiter. Es erkennt sogar alle Zeilen in der Datei richtig, löscht aber alle Integer-Werte in ihnen. –

+1

@GilDekel Ihre vorherige Frage hatte kein C++ - Label, also habe ich es erst gesehen, als ich dem Link gefolgt bin. Ich habe es auch beantwortet. Der Schlüssel ist, jedes Mal, wenn Sie eine neue Zeile erhalten, einen _new_ 'std :: istringstream' mit der Zeile zu konstruieren. –

1

Im Allgemeinen ist das Verhalten eines istream nicht in Stein gemeißelt. Es gibt mehrere Flags, um zu ändern, wie sich istream verhält, was Sie über here lesen können. Im Allgemeinen sollten Sie sich nicht wirklich darum kümmern, wo der interne Zeiger ist; Aus diesem Grund verwenden Sie überhaupt einen Stream. Andernfalls würden Sie einfach die gesamte Datei in eine Zeichenkette oder eine gleichwertige Datei ablegen und sie manuell überprüfen.

Wie auch immer, gehen Sie zurück zu Ihrem Problem, ein möglicher Ansatz ist die getline Methode von istream, um eine Zeichenfolge zu extrahieren. Von der Zeichenkette können Sie sie entweder manuell lesen oder in eine stringstream konvertieren und Token von dort extrahieren.

Beispiel:

std::ifstream ifs("myFile"); 
std::string str; 

while (std::getline(ifs, str)) { 
    std::stringstream ss(str); 
    double sum = 0.0, value; 
    while (ss >> value) sum += value; 
    // Process sum 
} 
+0

Danke. Wie ich in meiner Antwort auf @James Kanze erwähnt habe, habe ich etwas Ähnliches versucht [in meiner vorherigen Frage.] (Http://stackoverflow.com/questions/26134028/not-getting-all-lines-from-a-text- datei-wenn-benutze-getlin-in-c) Allerdings habe ich es wahrscheinlich auf eine dumme Weise vermasselt, da ich nie ein sstring benutzt habe. –

+1

@GilDekel Ich habe Ihren Code dort gesehen, und es sieht so aus, als würden Sie den Inhalt des 'stringstream' manuell durcheinanderbringen, um ihn zurückzusetzen. Ziehen Sie in Erwägung, wie in diesem Fall bei jedem Schleifenzyklus einen neuen zu erstellen. – Svalorzen