aus Textdatei arrays basteln
-
Hallo liebes Forum,
ich sitze schon seit Ewigkeiten über einem Problem...
ich habe eine Textdatei die folgende Zeilen hat (insgesamt 30 Zeilen) :
1,Max,Mustermann
2,Rolf,Muster
3,Pia,MusterNun würde ich daraus gerne 3 Arrays basteln:
id[0]=1 vorname[0]=Max name[0]=Mustermann
id[1]=2 vorname[1]=Rolf name[1]=Muster
id[2]=3 vorname[2]=Pia name[2]=MusterMeine Idee war es zuerst, mir die Datei mit Hilfe von ifstream und datei.open einzulesen (was auch klappt) und dann zeilenweise abzufragen wo da erste Komma ist, dann weiss ich das davor die ID stehen muss. Im nächsten Schritt dann nach dem nächsten Komma suchen,davor muss dann der vorname stehen usw.
Nur das bekomme ich nicht s hin, bzw. gibt es da eine bessere/schönere Lösung?
Danke schon jetzt für die Hilfe!
-
Da die Daten doch offensichtlich logisch zusammengehören, sollte man sie auch nicht trennen. Anstatt drei verschiedene Felder zu machen, machst du nur eines und der Datentyp ist dann irgendeine Klasse, welche die drei Attribute (Id, Vorname, Nachname) zusammen verwaltet. Das wird dir auch sehr wahrscheinlich später entgegen kommen, wenn du mit diesen Daten auch was machen willst.
Der Rest ist dann Standard: Eingabeoperator für diese Klasse definieren, der das vorgegebene Format lesen kann. Hier ist das Format mal in Worten ausgedrückt:
Integer; Komma; Zeichenkette; Komma; Zeichenkette; ZeilenumbruchInteger kann man einfach aus einem Stream lesen mit dem dazu vorgegebenen Operator. Ebenso einzelne Zeichen (hier das Komma), wir sollten nur prüfen, ob es wirklich ein Komma ist, damit wir merken, wenn etwas falsch ist. Dazu habe ich eine kleine Hilfsfunktion geschrieben, da man so etwas doch recht oft braucht (in diesem Programm jedoch nur einmal). Danach zwei Zeichenketten, die jeweils durch ein bestimmtes Trennzeichen beendet werden (einmal Komma, einmal Zeilenumbruch). Dafür bietet sich getline an.
All dies wird von der kleinen Funktion in Zeilen 27-33 zusammen gefasst.Der Rest des Programms ist ein kleines Beispiel um diese Funktionen gestrickt:
#include <iostream> #include <string> #include <vector> #include <sstream> // Für das Beispiel: Lesen aus String statt aus Datei // Kleine Funktion, die einen einzelnen Buchstaben liest und prüft, ob es ein bestimmter ist. template<char which> std::istream& readchar(std::istream& in) { char c; in >> c; if (in and c != which) { in.putback(c); in.setstate(in.failbit); } return in; } // Klasse, die die gewünschten Daten hält class DataEntry { int id; std::string first_name; std::string last_name; // Funktion, um Daten in dem gezeigten Format zu lesen: friend std::istream& operator>>(std::istream &in, DataEntry &data) { in >> data.id >> readchar<','>; getline(in, data.first_name, ','); getline(in, data.last_name); return in; } // Zum Testen eine Ausgabefunktion: friend std::ostream& operator<<(std::ostream &out, const DataEntry &data) { return out << "id = " << data.id << "\nVorname: " << data.first_name << "\nNachname: " << data.last_name << '\n'; } }; int main() { std::stringstream testdaten("1,Max,Mustermann\n2,Rolf,Muster\n3,Pia,Muster"); // Daten in Feld lesen: std::vector<DataEntry> data; for(DataEntry entry; testdaten >> entry;) data.push_back(entry); // Testausgabe: for(const DataEntry& entry: data) std::cout << entry << '\n'; }
-
SeppJ schrieb:
template<char which> std::istream& readchar(std::istream& in) { char c; in >> c; if (in and c != which) { in.putback(c); in.setstate(in.failbit);Weshalb machst du da ein
putbackSeppJ?
-
out schrieb:
Weshalb machst du da ein
putbackSeppJ?Weil ich den Zustand des Streams möglich genau wieder herstellen möchte, nachdem eine Extraktion logisch fehlgeschlagen ist.
-
Hall o SeppJ,
Vielen dank,da ich schon sehr lange nichts mehr programmiert habe, gehe ich an die Sachen oft etwas zu umständlich ran. habe Dein Programm gerade ausprobiert, mein Compiler hat folgendes zurückgemeldet: "error: range-based ‘for’ loops are not allowed in C++98 mode"...das beziehet sich auf die Schleife in der main.
Hast Du eien Idee warum er da meckert?
-
Gib mal deinem Compiler -std=c++11 mit, falls du g++ verwendest.
-
fischldi schrieb:
Vielen dank,da ich schon sehr lange nichts mehr programmiert habe, gehe ich an die Sachen oft etwas zu umständlich ran. habe Dein Programm gerade ausprobiert, mein Compiler hat folgendes zurückgemeldet: "error: range-based ‘for’ loops are not allowed in C++98 mode"...das beziehet sich auf die Schleife in der main.
Hast Du eien Idee warum er da meckert?
Weil es range based 'for' Schleifen in C++98 nicht gibt
. Das gibt es erst seit C++11. Da dein Compiler das Konstrukt erkennt, kann er offensichtlich C++11. Es bleiben zwei Wege:
-Du setzt du deinen Compiler in den C++11-Modus (das willst du kurz oder lang vermutlich sowieso). Was ist das für ein Compiler? GCC? Bei dem setzt du die Kommandozeilenoption-std=c++11oder bei älteren Versionen (vor 2011)-std=c++0x.
-Du schreibst den Code um. Er gibt alle Elemente in dem vector aus. Solltest du auch ohne range-based for hinbekommen, oder? Ist bloß mehr Schreibarbeit, weshalb ich es nicht so gemacht habe.
-
Hallo SeppJ,
danke, Dein Tipp mit dem g++ Compiler hat geholfen , ich kann es kompilieren

Das was Du mir geschrieben hast, klingt sehr nach dem was ich brauche!! Ich habe ja eine Textdatei in der die ganzen Datensätze liegen. Beim Versuch die Daten aus der Textdatei in das Programm einfliessen zu lassen, kam eine ellenlange Fehlermeldung.
Ich habe es folgendermassen probiert:
int main() { string s; ifstream namendatei; // Namensdatei iegt im verzeichnis namendatei.open("namen.txt",ios_base::in); //oeffne textdatei mit namen while(!namendatei.eof()) //solange noch daten vorliegen { getline(namendatei,s); //liess eine Zeile ein cout << s; // Daten in Feld lesen: std::vector<DataEntry> data; for(DataEntry entry; s >> entry;) data.push_back(entry); } // Testausgabe: for(const DataEntry& entry: data) std::cout << entry << '\n'; return 0; }Hat das etwas mit dem ifstream zu tun? Aber dieses Format brauche ich doch um aus Textdatein zu lesen??
Danke schonmal für eure Hilfe...wenn das geschafft ist bekomme ich den Rest alleine hin

-
Hi.
-Schau dir mal RAII an. Dann weißt du, warum du kein namendatei.open brauchst.
-Du musst nach einem Einelsevorgang prüfen, ob das Einlesen erfolgreich war. Also zwischen getline(namendatei,s) und cout << s; fehlt etwas.
-Wenn du DataEntry ausgeben willst, musst du operator<< überladen.
-
Du versuchst hier operator>> auf einen String und einen DataEntry anzuwenden, was irgendwie total sinnlos ist. Die Fehlermeldung sagt, dass es einen solchen Operator nicht gibt und schlägt dir alle möglichen Alternativen vor (daher ist die Meldung so lang).
Du hättest einfach nur den Stringstream aus dem Beispiel durch deine Datei ersetzen brauchen, dann hätte alles gepasst. Das schöne am IO-Modell von C++ ist doch, dass die ganzen Streamtypen untereinander austauschbar sind. Alles was du jetzt zusätzlich zum Code hinzu gefügt hast macht das Programm kaputt:
-Die Abbruchbedingungwhile(!eof)ist Schwachsinn. Das ist in keiner C-artigen Sprache richtig. Wo du das her hast, solltest du nicht weiter lesen. Derjenige, der dir das gezeigt hat, hat keine Ahnung.
-Der ganze Punkt den ich machen wollte war doch, dass man die Formatierung zentral in die Lesefunktion packen sollte. Das getline in der main und der anschließende (falsche, s.o.) Versuch auf die Zeile die Lesefunktion anzuwenden ist gerade das, was hier vermieden werden sollte.
-
Mal eine Frage:
Ist das hier
for(const DataEntry& entry: data)Equivalent zu
for(auto entry : data)oder gibt es da einen Unterschied und wenn ja, welcher?
(Abgesehen vom syntaktischen)
-
Ja, einmal ist das eine konstante Referenz auf DataEntry, ein andermal decltype(*data.begin()), was nicht unbedingt eine konstante Referenz sein muss, sondern eher eine nicht konstante Referenz.
-
Das heißt es ist garnicht so toll auto dort zu verwenden, ich meine, ist das überhaupt definiert ob const oder nicht? Und sollte man einfach "manuell" const schreiben, wenn man sicher sein will, dass man da nix verändert?
-
Nathan schrieb:
konstante Referenz
ALARM ALARM
-
hardware schrieb:
Das heißt es ist garnicht so toll auto dort zu verwenden, ich meine, ist das überhaupt definiert ob const oder nicht? Und sollte man einfach "manuell" const schreiben, wenn man sicher sein will, dass man da nix verändert?
auto ist dazu da, dass du das Denken dem Compiler überlassen kannst. Es ist immer sinnvoll, das Denken jemanden zu überlassen, der es zu 100% weiß. auto steht dann für den richtigen Datentypen. const hat mit auto nichts zu tun. const ist kein Datentyp.
-
out schrieb:
Nathan schrieb:
konstante Referenz
ALARM ALARMJa, ja.
Referenz auf const.

-
Hallo,
danke für Eure Beiträge!
Das meine while-Schleife mit der != eof() nicht schön ist, verstehe ich. Habe dafür aber gerade noch keine Alternative gefunden, denn ich muss meine Textdatei ja zeilenweise einlesen und brauche dafür eine Schleife?
Ich habe versucht , die Datei in einen String einzulesen und diesen String dann in einen stringstream umzuwandeln, Fehlermeldung bekomme ich keine ,aber auch keine Ausgabe...
Ich glaube das der letzte Schritt einfach ist, aber ich finde das ganze Thema schwierig und stehe da gerade auf dem Schlauch...
int main() { string s; ifstream namendatei; // liegt im verzeichnis namendatei.open("namen.txt",ios_base::in); //oeffne textdatei mit namen stringstream ss(s); vector<DataEntry> data; //Vektor data der Klasse DataEntry angelegt while(!namendatei.eof()) //solange noch daten vorliegen { getline(namendatei,s); //liess eine zeile ein //stringstream ss("1,Max,Mustermann\n2,Rolf,Muster\n3,Pia,Muster"); for(DataEntry entry; ss >> entry;) data.push_back(entry); } // Testausgabe: for(const DataEntry& entry: data) std::cout << entry << '\n'; return 0; }
-
int main() { ifstream namendatei("namen.txt"); vector<DataEntry> data; for(string s; getline(namendatei,s); ) { stringstream ss(s); for(DataEntry entry; ss >> entry; ) data.push_back(entry); } for(const DataEntry& entry: data) std::cout << entry << '\n'; return 0; }sollte funktionieren
LG
-
fischldi schrieb:
Das meine while-Schleife mit der != eof() nicht schön ist, verstehe ich. Habe dafür aber gerade noch keine Alternative gefunden, denn ich muss meine Textdatei ja zeilenweise einlesen und brauche dafür eine Schleife?
Nicht unschön, sondern schlichtweg falsch. Wenn das in deinem Lehrbuch so stand (kommt leider vor), dann schmeiß es weg. Wenn du einen Fehler beim Lesen hast, dann verarbeitest du hier Mülldaten, erst dann folgt deine Prüfung ob das Lesen erfolgreich war. Und nicht einmal das, denn du prüfst nur auf Dateiende, bei anderen Fehlern hättest du eine feine Endlosschleife der Müllverarbeitung.
Lesen läuft in C++ (und anderen C-artigen Sprachen) immer so ab:
-einlesen
-prüfen
-verarbeitenEinen Stream kann man ganz einfach auf einen Fehlerzustand prüfen (und zwar alle möglichen Fehler, nicht nur eof!), indem man ihn in einem boolschen Kontext auswertet:
int i; cin >> i; if (cin) ... // erfolgreich?Schönerweise geben fast alle Leseaktionen in C++ den Stream selbst zurück, so dass man die Leseaktion und die Prüfung schick zusammen fassen kann:
int i; if (cin >> i) ... // erfolgreich?So etwas kann man dann naheliegenderweise auch als Abbruchbedingung für Schleifen benutzen. Oft gesehene Konstrukte:
int i; while (cin >> i) { // i verarbeiten } // oder for(int i; cin >> i; ) { // i verarbeiten }Dies ist ebenfalls etwas, das in einem Lehrbuch erklärt gehört, wenn das nicht drin steht, dann hat das irgendjemand geschrieben, der vor 30 Jahren Mal Pascal gelernt hat (da geht das tatsächlich so wie du es versuchst) und denkt, C++ wäre wenn er
BEGINundENDdurch{und}ersetzt.
-
SeppJ hat die perfekte Lösung bereits gepostet. Wieso nimmst du diese nicht und versuchst alles nachzuvollziehen? Dann hast was gutes gelernt. Wenn was an seinem Code nicht verstehst kann man auf jene Zeile ja eingehen hier.