Unerwünschte Textzeile beim auslesen löschen - str.erase() funktioniert nicht!?
-
Das ist die Ausgabe:
Texdatei Nr1
Klausurnoten
********************************************************
Drücken Sie eine beliebige Taste . . .Also ich habe gelernt, dass !eof() solange noch Daten gelesen werden können, bedeutet.
Ich hab jetzt mal danach gesucht und da steht: Das eofbit wird gesetzt, nachdem
eine Leseoperation das Ende der Eingabe erreicht hat.Trotzdem bringt mich das nicht groß weiter. Wenn das alles falsch ist, kannst du mir ja zeigen wie man's richtig macht.

-
Danke hustbaer, ich werde mir das nochmal genauer durchlesen und mich dann nochmal melden.

-
Array schrieb:
Das ist die Ausgabe:
Texdatei Nr1
Klausurnoten
********************************************************
Drücken Sie eine beliebige Taste . . .Von welchem Code? Und was folgerst du daraus?
Geh mal deinen kompletten Code durch und lass dir an jeder Stelle des Programmverlaufs anzeigen (vorzugsweise mit einem Debugger, zur Not mit Ausgaben), welchen Wert welche Variablen haben. Vergleiche deine Ergebnisse mit deinen Erwartungen. Du wirst feststellen, dass das vorne und hinten nicht passt.
Mit kleinen Korrekturen ist es jedoch in diesem Fall nicht getan. Wie hustbaer empfohlen hat, solltest du dich mal kundig machen, was die Funktionen, die du aufrufst, überhaupt genau tun (Der Operator >> ist auch eine dokumentierte Funktion!). Danach kannst du nachdenken, wie dein Programm tatsächlich aussehen muss.
Also ich habe gelernt, dass !eof() solange noch Daten gelesen werden können, bedeutet.
Ich hab jetzt mal danach gesucht und da steht: Das eofbit wird gesetzt, nachdem
eine Leseoperation das Ende der Eingabe erreicht hat.Trotzdem bringt mich das nicht groß weiter.
Das heißt, deine Schleife bricht dann ab, wenn bereits eine Leseoperation fehlgeschlagen ist. Da du innerhalb deiner Schleife aber fröhlich Leseoperationen durchführt und die Ergebnisse ungeprüft verarbeitest, wirst du beim letzten Durchlauf irgendwelchen Unsinn verarbeiten, weil das Lesen fehlschlug und du erst viel zu spät darauf reagierst.
Wenn das alles falsch ist, kannst du mir ja zeigen wie man's richtig macht.

Typische Leseschleife in C-artigen Sprachen:
while(erfolgreich(leseaktion(quelle, daten))) verarbeite(daten);Konkret für C++ geben die meisten (aber nicht alle! Referenz nutzen!) Leseaktionen ihre Quelle (den Stream) selbst zurück. Diese könntest du dann mit eof prüfen a la
while(!(getline(quelle, ziel).eof())), aber die Streams werden auch automatisch in solchen Kontexten auf ihre Fehlerflags geprüft (und zwar auf alle Fehlerflags, nicht nur eof!). Also üblicherweise etwas wie:while(getline(quelle, ziel)) verarbeite(ziel);Oder vergleichbare Konstrukte.
Das ist aber tendenziell eher ein Nebenschauplatz. Was die Lesefunktionen an sich genau machen und wie Zeichenketten funktionieren ist hier erst einmal ein viel wichtigeres Thema. Das mit dem eof hast du jetzt einmal gesehen und hoffentlich gelernt. Das andere ist viel mehr für dich zu lernen und du musst dann noch lernen, wie man alles richtig zusammen fügt (das mache ich auch bewusst nicht vor, weil du es sonst nicht lernen würdest). Ich wollte nur feststellen, ob das Problem eher bei dir oder deinem Lehrer liegt.
-
Hallo Array,
Array schrieb:
Also ich habe gelernt, dass !eof() solange noch Daten gelesen werden können, bedeutet.
kannst Du uns noch mitteilen, von wem Du das gelernt hast.
Sobald man zwischen Lesefehler (FAIL) und EOF unterscheiden kann, ist dies in jeder Programmiersprache falsch. Korrekt ist immerif(!FAIL), aber natürlich bitteschön nach(!) dem Lesen prüfen.Ich weiß, dass Code-Beispiele mit
while(!inputStream.eof())100-tausend-fach im I-Net zu finden sind. Dieser Hoax wird uns wohl noch alle überleben.Gruß
Werner
-
Wurde mir so im Tutorium beigebracht. Hab es wie du schon sagst auch schon öfter auf Seiten im Netz gelesen, deswegen hab ich das auch nicht weiter angezweifelt.
Also: Wenn ich das jetzt richtig verstanden hab, muss ich zuerst einen Test auf if(!quelle.good()) machen. Dieser gibt true zurück wenn eof, badbit oder failbit gesetzt werden.
if(quelle.good()) { getline(quelle, einlesen); }Wobei mir jetzt immer noch nicht klar ist warum ich kein getline benutzen soll. Getline liest doch eine Zeile bis zum '\n' ein und speichert sie in einlesen. Was ja mein Ziel ist, weil ich den Variablen "einlesen" übergebe.
Wenn ich dann eine Zeile in meinem String habe, kann ich den ja mittels einlesen.find("") auf das Zeichen "" überprüfen. Die Methode liefert mir die Position des ersten aufkommens des Zeichens. Statt dem einlesen.erase() hab ich eine bessere Methode gefunden, nämlich das einlesen.clear(), was mir die ganze Zeile löscht. (Ich mache die Abfrgae, falls mehrere solcher Zeichen auftauchen sollten)
Wenn einlesen nicht das Zeichen "*" hat, tue dies, andernfalls, lösche die Zeile;
if (einlesen.find("*") == string::npos) { getline(quelle, einlesen, '\n'); getline(quelle, einlesen, ';'); name = einlesen; getline(quelle, einlesen, ';'); matrikelnummer = einlesen; getline(quelle, einlesen, ';'); note = einlesen; } else { einlesen.clear(); }http://www.cplusplus.com/reference/string/string/clear/
Natürlich funktioniert es immer noch nicht. Ich werde mir das nochmal genauer ansehen aber kann mir mal nicht jemand genau sagen warum ich was nicht benutzt soll bzw. in dem Fall falsch ist? Das würde mich schon ziemlich weiterbringen.
-
Hallo Array,
Array schrieb:
Also: Wenn ich das jetzt richtig verstanden hab, muss ich zuerst einen Test auf if(!quelle.good()) machen. Dieser gibt true zurück wenn eof, badbit oder failbit gesetzt werden.
if(quelle.good()) { getline(quelle, einlesen); }Nö
Werner Salomon schrieb:
Korrekt ist immer
if(!FAIL), aber natürlich bitteschön nach(!) dem Lesen prüfen.Also genau wie SeppJ schon ausgeführt hat:
if( getline(quelle, einlesen) ) { // bzw. while( getline(quelle, einlesen) ) // 'einlesen' bearbeitengetlinegibt hier eine Referenz auf 'quelle' zurück und die Abfrage (ifoderwhile) liefert ein!quelle.fail().. und natürlich nachdem(!) versucht wurde einen Zeile aus 'quelle' zu lesen!
Übrigens kann der Input Stream dann auch nach EOF gehen, d.h. ein quelle.eof() würde true liefern, nämlich genau dann, wenn die letzte Zeile nicht mit einem LF(Linefeed) abgeschlossen ist. Trotzdem ist das eine Zeile und die Lese-Operation war Ok.Array schrieb:
Wurde mir so im Tutorium beigebracht.
Schönen Gruß an Deinen Dozenten, dann kläre ihn mal auf

Array schrieb:
Wobei mir jetzt immer noch nicht klar ist warum ich kein getline benutzen soll.
Weil Du in Deinem Beispiel zumindest am Anfang
getlinezum Überspringen von Zeilen einsetzt, nicht zum Lesen. Hier wäre eine ignore angebracht.
getlineliest auch wirklich jede Zeile - also auch Leerzeilen - u.U. ist hier immer einquelle >> std::wsvor demgetlineangebracht, was alle Leerzeichen, Leerräume und Tabs überliest.
Ich kann Deine Datei leider nicht öffnen, daher weiß ich nicht was Du lesen möchtest.Das
erasein Zeile 23 löscht nur ein Zeichen ineinlesen, aber nie die Zeile.eraseist hier eine Methode vonstd::string, daeinleseneinstd::stringist.Gruß
Werner
-
.. inzwischen konnte ich Deine Datei lesen. Ich habe Dir das hier mal skizziert, wie man diese Daten einliest. Wobei ich mich an Deinen Code angelehnt habe .. in produktivem Code würde man das wohl noch etwas anders machen, aber ich wollte das nicht überfrachten.
Melde Dich doch bitte und sage uns, wie Du damit zurecht kommst.
Gruß
Werner#include <iostream> #include <fstream> #include <string> #include <limits> // numeric_limits<> int main() { using namespace std; const std::streamsize ALLES = numeric_limits< streamsize >::max(); ifstream quelle("Noten.txt"); // [i]fstream reicht if( !quelle.is_open() ) // IMMER prüfen, in 80% aller Fehlerfälle kann die Datei nicht gefunden werden! { cerr << "Fehler beim Oeffnen der Datei" << endl; return -2; } for( int zeile=0; zeile<4; ++zeile ) quelle.ignore( ALLES, '\n' ); // überspringe die Zeile string name; string matrikelnummer; double note; char c; while( quelle >> c ) // bricht ab bei Lesefehler { // auf '*' prüfen if( c == '*' ) { quelle.ignore( ALLES, '\n' ); // Kommentar überspringen continue; // oder break; ?? } quelle.putback( c ); // wieder zurück in den Stream, wir brauchen das Zeichen noch! getline( quelle, name, ';' ); getline( quelle >> ws, matrikelnummer, ';' ); // das 'ws' überliest das Leerzeichen vor 'matrikelnummer' quelle >> note; quelle.ignore( ALLES, '\n' ); // und den Rest der Zeile wieder überspringen // jetzt erst prüfen, ob das alles ok war! if( quelle ) { // hier kannst Du jetzt sicher sein, dass alle Variablen korrekt eingelesen wurden cout << name << "; " << matrikelnummer << "; " << note << endl; } else break; // Lesefehler, also Schleife beenden } return 0; }
-
Vielen Dank, Werner. Ich habe das Programm gleich mal getestet. Werde es aber mit dem Debugger nochmal durchgehen müssen und mir 1-2 Funktionen nochmal genauer anschauen um es richtig zu verstehen.
-
Hallo Werner, ich habe mich jetzt länger mit Dateiverarbeitung beschäftigt und Recht hattet ihr. Ich überprüfe jetzt immer erst ob auf if(!quelle.is_open()) und in der while-Schleife mache ich die Überprüfung auf while(quelle.good()) und in dieser dann erst die Prüfung auf while(!quelle.eof()). Das hat mir sehr viel Zeit und Fehler erspart. Vielen Dank erstmal für die hilfreichen Tipps.

Nur habe ich das mit den limits nicht ganz verstanden. Könntest du mir das vielleicht noch erklären?
#include <iostream>
#include <fstream>
#include <string>
#include <limits> // numeric_limits<>using namespace std;
const std::streamsize ALLES = numeric_limits< streamsize >::max();for( int zeile=0; zeile<4; ++zeile )
quelle.ignore( ALLES, '\n' ); // überspringe die Zeilewhile( quelle >> c ) // bricht ab bei Lesefehler
{
// auf '' prüfen
if( c == '' )
{
quelle.ignore( ALLES, '\n' ); // Kommentar überspringen
continue; // oder break; ??
}Und dann evtl. noch diese Zeile?
Warum wird das genau gemacht, was bewirkt es in diesem Fall konkret?quelle.putback( c ); // wieder zurück in den Stream, wir brauchen das Zeichen noch!
}
Vielen Dank schonmal.

-
Hallo Array,
Array schrieb:
Nur habe ich das mit den limits nicht ganz verstanden. Könntest du mir das vielleicht noch erklären?
Es gibt die Methode ignore. Mit dem ersten Parameter gibst Du die Anzahl der Zeichen an, die Du maximal ignorieren bzw. überlesen möchtest. Für den Fall dass dort die größte für diesen Typ darzustellende Zahl steht, heißt das so viel wie 'unendlich'. D.h. dann ist die Anzahl nicht begrenzt und
ignorearbeitet sich bis zu dem Zeichnen durch, welches gleich dem zweiten Parameter (hier '\n' bzw. EOL bzw. End-Of-Line) ist oder eben bis zum Ende der Datei.Da es in C++ keine eindeutige Festlegung gibt, wie groß so ein Typ werden kann, gibt es die Struktur std::numeric_limits. Dort kann man verschieden Eigenschaften arithmetischer Typen abfragen.
Undstd::numeric_limits<std::streamsize>::max()liefert eben die größte Zahl, die mit dem Typstd::streamsizedarstellbar ist. Das ist bei einem 32Bit-System i.A. =2147483647 (=0x7FFFFFFF); kann bei einem 64Bit-System aber auch =9223372036854775807 (=0x7FFFFFFFFFFFFFFF) sein.Array schrieb:
Und dann evtl. noch diese Zeile?
Warum wird das genau gemacht, was bewirkt es in diesem Fall konkret?quelle.putback( c ); // wieder zurück in den Stream, wir brauchen das Zeichen noch!
Schau hier nach: putback; oder lass die Zeile mal weg und schau Dir an, was dann passiert
. putback stellt das Zeichen 'c' wieder zurück, so dass es noch einmal gelesen werden kann. Zu beachten ist, dass das nicht beliebig oft geht! Am zuverlässigsten funktioniert es, wenn Du vorher genau ein Zeichen mit ' quelle >> c' gelesen hast; das ist hier der Fall.Array schrieb:
.. Ich überprüfe jetzt immer erst ob auf if(!quelle.is_open()) und in der while-Schleife mache ich die Überprüfung auf while(quelle.good()) und in dieser dann erst die Prüfung auf while(!quelle.eof()).
Hm! - noch mal zur Klarstellung: Im Prinzip sollte ein Code zum Lesen immer so aussehen:
Datei öffnen prüfen mit is_open() Schleife // ggf. for(;;) Lesen eines vollständigen Eintrags (hier von name bis note) // und jetzt erst: if( quelle ) // bzw. if( !quelle.fail() ) // ist das gleiche Ok, eingelesene Daten verwenden else Einlesen beenden Ende der Schleife // Wenn eine Datei bis zum Ende gelesen werden soll, dann und nur dann if( quelle.eof() ) Ok, Datei bis zum Enden gelesenDer Schleifenkopf inklusive Lesen plus prüfen, lässt sich in eine einzige Zeile zusammen fassen, wenn man Name, Matrikelnummer und die Note in einer Struktur 'Eintrag' zusammen fasst - etwa so
Eintrag eintrag; while( quelle >> eintrag ) { .. eingelesene Daten 'eintrag' verwendenGleichwertig ist:
for( Eintrag eintrag; quelle >> eintrag; ) { .. eingelesene Daten 'eintrag' verwendenGruß
Werner
-
Vielen lieben Dank an alle und vor allem dir Werner! Das hat mich wirklich weiter gebracht. Jetzt muss ich nur noch das mit dem überladen des Ausgabeoperators lernen und dann kann die Klausur auch kommen.
