Daten aus Textdokument auslesen und in vector schreiben
-
Hallo,
ich möchte Informationen aus einem Textdokument (inFile.txt) einlesen und diese in einem Vector speichern. Ich programmiere in Visual C++ 2008!
Ich finde sehr viele Ansätze zu diesem Thema aber ich möchte dies umsetzen mit Hilfe von Streams und es so übersichtlich und einfach halten wie nur möglich.
Aufbau der inFile.txt
Andreas Müller (string oder char)
95 (int)
1 (int)Fussball Verein HSV
8763
34Kegelclub SIXT
1264
15Ich bin also offen für jegliche Tipps und Vorschläge!
Vielen Dank im Voraus
-
jede Zeile in einen stringstream schreiben und von dort mit
operator>> in den entsprechenden Datentyp.
-
für die erste zeile bietet sich std::getline (aus dem header string - nicht die andere hässliche überladung mit dem char-gefrickel^^)
die beiden nächsten zeilen gehen einfach mit dem op>>(istream&, int) einzulesen.
bb
-
Ich nehme mal an, dass die Zahlen irgendwie zu dem String gehören? Dann brauchen wir erstmal eine Klasse dafür. Da ich in diesem Beispiel faul bin, nehme ich ein struct, ohne mich um Kapselung zu kümmern:
struct Datensatz { string Name; int ErsterInt; int ZweiterInt; };Diesem verpassen wir auch gleich noch einen Leseoperator. Weil Mischen von zeilenweisem Lesen und formatiertem Lesen nie gut kommt, machen wir alles mit getline und wandeln erst danach in int um:
bool strtoi(string in, int &out) { stringstream parser; parser << in; parser >> out; return parser; } istream& operator>>(istream&in, Datensatz& out) { string name; while (getline(in,name) && name.size()==0); // Leere Zeilen überspringen string erster, zweiter; getline(in, erster); getline(in, zweiter); if (in) // Nur etwas tun, wenn Lesen erfolgreich war { int ersterint, zweiterint; if (strtoi(erster,ersterint) && strtoi(zweiter,zweiterint)) // Und auch tatsächlich Zahlen gelesen wurden { out.Name=name; out.ErsterInt=ersterint; out.ZweiterInt=zweiterint; } else in.setstate(ios::failbit); // Ansonsten ist was schiefgegangen } return in; }Und der Form halber auch eine Ausgabe:
ostream& operator<<(ostream&out, Datensatz& in) { out<<"Name: \t"<<in.Name<<"\nZahl1: \t"<<in.ErsterInt<<"\nZahl2: \t"<<in.ZweiterInt; return out; }Und dann sollte das auch schon flutschen:
int main () { vector<Datensatz> daten; { // Einlesen ifstream file("test.txt"); Datensatz temp; while(file>>buffer) daten.push_back(temp); } // Testausgabe: for (int i; i<daten.size(); ++i) cout<<daten[i]<<endl; }Und das hat jetzt viel zu lange gedauert, mal sehen, wie viele hundert Antworten du mittlerweile bekommen hast.
-
Hallo PauleJS,
willkommen im C++-Forum.
Wenn Du das in einen vector einlesen möchtest, solltest Du zunächst eine Klasse/Struktur implementieren. Nimm dabei keine Rücksicht darauf, wie die Daten in der Datei stehen, sondern nur darauf, wie Du später am besten mit der Klasse/Struktur arbeiten kannst. Da ich nicht weiß, was das werden soll, nenne ich diese Klasse/Struktur mal 'Foo'.
Anschließend verpasst Du Foo einen sogenannten Streaming-Iterator, der es möglich macht, ein Foo Objekt aus einem Stream zu lesen. Idealerweise als friend-Funktion, da auch private Member gefüllt werden müssen. Folgende Anwendung soll dann möglich sein:
ifstream datei( "inFile.txt" ); Foo einFoo; if( datei >> einFoo ) { // einFoo korrekt gelesenJetzt brauchst Du den vector, der Objekte vom Typ Foo enthalten soll, nur noch aus der Datei füllen, wobei ich hier den istream_iterator<> verwende. Alles zusammen sieht das z.B. so aus:
#include <fstream> #include <iostream> #include <string> #include <iterator> // istream_iterator #include <vector> class Foo { public: Foo() : m_name() , m_i1() , m_i2() {} // .. hier bitte alle Methoden hinzufügen, die noch notwendig sind // -- Streaming Operator, der ein Foo-Objekt vom std::istream liest friend std::istream& operator>>( std::istream& in, Foo& foo ) { return getline( in >> std::ws, foo.m_name ) >> foo.m_i1 >> foo.m_i2; } private: std::string m_name; int m_i1; int m_i2; }; int main() { using namespace std; ifstream datei( "inFile.txt" ); if( !datei.is_open() ) { cerr << "Fehler beim Oeffnen der Datei" << endl; return -1; } vector< Foo > foos( (istream_iterator< Foo >( datei )), istream_iterator< Foo >() ); // einfach so einlesen if( !datei.eof() ) // falls das Ende der Datei nicht erreicht wurde, so wurde vorher wegen Fehler abgebrochen { cerr << "Fehler beim Lesen" << endl; return -1; } // 'foos' verwenden cout << foos.size() << " Eintraege gelesen" << endl; return 0; }den Namen der Klasse Foo und die Namen der Member solltest Du äquivalent zu Ihrer Bedeutung ändern.
Falls irgendwas unklar ist, bitte nachfragen.SeppJ schrieb:
Weil Mischen von zeilenweisem Lesen und formatiertem Lesen nie gut kommt, machen wir alles mit getline ..
wieso das denn?
Gruß
Werner
-
Werner Salomon schrieb:
SeppJ schrieb:
Weil Mischen von zeilenweisem Lesen und formatiertem Lesen nie gut kommt, machen wir alles mit getline ..
wieso das denn?
Das führt dann immer zu mit Whitespace gefüllten Zeilen. Genau das war nämlich mein Problem, weshalb ich das so gemacht habe. Gib mir mal eine vernünftige Lösung, wie man bei diesem Beispieldatensatz mit einer Mischung von getline und >> vernünftig einliest. Ich werde dir mal das Leerzeichen hervorheben, das ein menschlicher Dateiersteller dort versehentlich hinterlassen hat:
Andreas Müller 95 1_ <-- Hier eine nette Problemstelle Fussball Verein HSV 8763 34
-
SeppJ schrieb:
Werner Salomon schrieb:
SeppJ schrieb:
Weil Mischen von zeilenweisem Lesen und formatiertem Lesen nie gut kommt, machen wir alles mit getline ..
wieso das denn?
Das führt dann immer zu mit Whitespace gefüllten Zeilen.
.. ein Grund mehr, stets formatiert zu lesen, solange es möglich ist. getline liest dagegen unformatiert.
SeppJ schrieb:
Genau das war nämlich mein Problem, weshalb ich das so gemacht habe. Gib mir mal eine vernünftige Lösung, wie man bei diesem Beispieldatensatz mit einer Mischung von getline und >> vernünftig einliest. Ich werde dir mal das Leerzeichen hervorheben, das ein menschlicher Dateiersteller dort versehentlich hinterlassen hat:
Andreas Müller 95 1_ <-- Hier eine nette Problemstelle Fussball Verein HSV 8763 34Die Lösung siehst Du bereits in meinem vorher geposteten Code. Da ist es ziemlich egal, wie viel Leerzeichen hinter den Zahlen stehen, oder wie viel Leerzeilen die einzelnen Datensätzen trennen. Der Trick liegt in dem
in >> std::wsin Zeile 20. Mit dem std::ws wird alles an White Space Zeichen überlesen, was vor dem ersten Text des Datensatzes steht.
Gruß
Werner
-
Vielen Dank euch allen für die ganzen Tipps und Ratschläge!!
Einen besonderen Dank muss ich Werner Salomon geben! Ich möchte deinen Code nun eigentlich gerne in mein bestehendes Programm einbinden! Dies funktioniert leider nicht wie gewünscht.
Zum Verständnis hier mal den Code und die Beschreibung
Beschreibung:
Eine Reiseagentur organisiert Flüge in einem Flugzeug das maximal 10.000 Kg (Passagiere + Gepäck) aufnehmen kann. Die Reservierung beinhaltet für jeden Passagier drei Eingabewerte:1. Eine Identifikationsnummer für den Passagier (5 stellige Zufallszahl)
2. Das Gesamtgewicht des Passagiere + Gepäck
3. Einen Gruppecode der angibt, ob der Passagier individuell reist, oder innerhalb einer Gruppen (0 bedeutet eine individuelle, >0 bedeutet in Gruppen)Ablauf:
1. Die Passagier-Daten in einen Vector einlesen (inFile.txt)
2. Alle Passagiere die insgesamt nicht das maximal Gewicht von 10.000 Kg berschreiten werden in eine Ausgabedatei outFile.txt ausgegeben.
3. Die Daten der Passagiere, die nicht beitreten können sollen in die Datei Annulierungen.txt geschrieben werden#include "Vliegreizen.h" #include "iostream" using namespace std; int main() { Vliegreizen v; cout << "HD13 - Vliegreizen" << endl; cout << endl; v.loadData(); // daten einlesen aus der inFile.txt //v.reserveringen(aantal); //v.toFile("uitvoerFile.txt"); system("PAUSE"); return 0; }#include <vector> #include <string> #include <fstream> #include <iostream> #include <iterator> // istream_iterator #include "Reiziger.h" using namespace std; class Vliegreizen { private: int gewichtTotaal; int groepsKode; // zweite int zahl in der inFile.txt int gewicht; // erste int zahl in der inFile.txt int aantal; // anzahlt der passagiere std::string name; // name in der inFile.txt vector<Reiziger> r; // dieser vector soll genutzt werden public: Vliegreizen(); void loadData(); void addReiziger(); // daten in vector hinzufügen void toFile(); // neue daten sollen in void annuleren(); // restliche passagiere in Annulierungen.txt schreiben };#include "Vliegreizen.h" using namespace std; Vliegreizen::Vliegreizen(void){ int gewichtTotaal = 0; int gewicht = 0; int aantal = 0; } void Vliegreizen::loadData(){ ifstream file( "inFile.txt" ); if( !file.is_open() ) { cerr << "Cannot open file" << endl; return -1; } vector< Vliegreizen > v( (istream_iterator< Foo >( datei )), istream_iterator< Foo >() ); // einfach so einlesen if( !file.eof() ) // falls das Ende der Datei nicht erreicht wurde, so wurde vorher wegen Fehler abgebrochen { cerr << "Error reading" << endl; return -1; } // 'foos' verwenden cout << foos.size() << " readed entries" << endl; vector <Foo>::iterator pos; }#include "Reiziger.h" using namespace std; Reiziger::Reiziger (){ int id = 0; int gewicht = 0; } int Reiziger::random(){ int min=10000; int max=99999; srand((unsigned int)time(0)); id =rand()%(max-min+1)+min; //Berechnung der Zufallszahl im gewünschten Bereich return id; } string Reiziger::toString() { }#include <list> #include <string> #include <cstdlib> #include <ctime> using namespace std; class Reiziger { private: int id; int gewicht; public: Reiziger(); int random(); string toString(); };
-
PauleJS schrieb:
... Ich möchte deinen Code nun eigentlich gerne in mein bestehendes Programm einbinden! Dies funktioniert leider nicht wie gewünscht.
Hallo PauleJS,
man kann es gar nicht oft genug schreiben: 'funktioniert nicht' ist nun mal keine ausreichende Fehlerbeschreibung!
Trotzdem ein paar Bemerkungen zu der Methode Vliegreizen::loadData().
- Die Variable ifstream wird in Zeile 13 angelegt; in Zeile 19 versuchst Du dann aber aus der Variable 'datei' zu lesen. Das sollte einen Compiler-Fehler geben. In Zeile 19 muss statt 'datei' 'file' stehen.
- Die Methode liefert als Return-Typ void, aber es steht zweimal return -1; (also Return-Typ int) in der Datei. Auch das geht nicht und sollte zu einem Compile-Fehler führen.
- Die Variable 'v' (Zeile 19) ist lokal und geht am Scope-Ende der Methode verloren. Damit ist das Einlesen der Datensätze obsolet
- Zeile 28 ist sinnlos.Versuche erst einmal diese Compile-Fehler zu beseitigen und teile uns dann mit, was genau dann vielleicht immer noch nicht geht. Und mache 'v' zu einem Member Deiner Klasse.
Gruß
Werner
-
ich habe es nun so verstanden das folgende zeile alle daten aus der datei in den vector einliesst:
vector<Vliegreizen> v( (istream_iterator< Vliegreizen >( file )), istream_iterator< Vliegreizen >() );ich möchte mir jetzt einfach ausgaben lassen was in dem vector ist
vector <Vliegreizen>::iterator pos; for (pos = v.begin(); pos != v.end(); ++pos){ cout << *pos << endl; }bekomme hier aber immer folgende fehlermeldung:
error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'Vliegreizen' (or there is no acceptable conversion)ich verstehe nicht wo der fehler ist!
-
Na, steht doch da: Es wird kein Operator namens << gefunden, der auf der rechten Seite ein Objekt vom Typ Vliegreizen nimmt. Denk dir doch mal, was bei folgendem Code passieren würde:
Vliegreizen foo; cout << foo;Würde das funktionieren? Nein, das würdest du auch gar nicht erst versuchen. Was du nämlich vorher brauchst, ist eine entsprechende Überladung für den Operator <<. Dies kann zum Beispiel so aussehen:
ostream& operator<<(ostream &out, const Vliegreizen& foo) { // Hier gibst du foo in out aus, so wie du es dir vorstellst return out; }Dann funktioniert das auch. Achte dabei auf die Sichtbarkeit der Member von foo, denn es handelt sich bei dem Operator um eine freie Funktion.
-
gibt es niemanden der mir weiter helfen kann?
-
PauleJS schrieb:
gibt es niemanden der mir weiter helfen kann?
Du hast doch eine Erklärung des Problems und einen Lösungsansatz erhalten.
-
PauleJS schrieb:
gibt es niemanden der mir weiter helfen kann?
Hallo PaulJS,
in der Antwort von SeppJ steht doch schon die Lösung für Dein Problem. Wenn Du was nicht verstehst, so musst Du schon konkreter fragen.Gruß
Werner
-
tut mir leid wegen dem letzten kommentar, habe immer nur die erste seite gesehen!
ich glaube ich habe nun langsam verstanden wie es funktioniert!
wollte das nun noch mit einer toString methode lösen, wo die ausgabe definiert wird.ist dieser ansatz so richtig? oder gibt es einfachere wege hierbei zum ziel zu kommen?
std::ostream& operator<<(std::ostream& os, const Vliegreizen& v){ os << v.toString(); return os; }ausserem bekomme ich hier noch die fehlermeldung:
error C2601: 'operator <<' : local function definitions are illegal
-
PauleJS schrieb:
ist dieser ansatz so richtig? oder gibt es einfachere wege hierbei zum ziel zu kommen?
Das ist eine gute Idee und auch ein sehr häufig benutzter Ansatz. Die andere übliche Methode ist es, den Operator zum friend der Klasse zu machen.
ausserem bekomme ich hier noch die fehlermeldung:
error C2601: 'operator <<' : local function definitions are illegalDas geht aus dem Code nicht hervor, woran das liegt. Es klingt danach, als würdest du versuchen den Operator innerhalb einer anderen Funktion zu definieren. Überprüf mal, ob alle Klammern in deinem Programm richtig gesetzt sind.
-
da mein code total unstrukturiert war, habe ich einfach neu angefangen!
habe aber nun am anfang schon direkt tolle fehlermeldungen bekommen:
error C2512: 'std::vector' : no appropriate default constructor available
vliegreizen.h(11) : error C2065: 'string' : undeclared identifier
vliegreizen.h(11) : error C2065: 'string' : undeclared identifierheader vliegreizen
#include <fstream> // open file #include <iostream> #include <vector> class Vliegreizen{ private: int gewicht; int gewichtTotaal; int id; int groepsKode; std::vector<string> data; public: Vliegreizen(); void loadAanvragen(); void verwerkAanvragen(); void reserveringen(); void annuleringen(); };#include "Vliegreizen.h" using namespace std; Vliegreizen::Vliegreizen(){ int gewicht = 0; int gewichtTotaal = 0; } void Vliegreizen::loadAanvragen(){ ifstream readFile( "Aanvragen.txt" ); if( !readFile.is_open() ) { cout << "Cannot open file" << endl; } else { cout << "Open file" << endl; } char zeile [30]; // max 30 characters while(readFile.getline (zeile, 500)){ //data.push_back(zeile); } readFile.close(); }ich verstehe nicht was ich hier falsch gemacht haben soll! in meinem buch kann man einen vector so benutzen.
-
PauleJS schrieb:
error C2512: 'std::vector' : no appropriate default constructor available
vliegreizen.h(11) : error C2065: 'string' : undeclared identifier
vliegreizen.h(11) : error C2065: 'string' : undeclared identifierSchau dir einmal diese 3 Fehlermeldungen an. C2065 bedeutet : "Der angegebene Bezeichner wurde nicht deklariert".
Du includierst zwar<vector>, jedoch nicht<string>. Da du außerdem vernünftigerweise keinusing namespace std;in die Header-Datei geschrieben hast, sollte es anschließend auchstd::vector<std::string> data;heißen.
-
Vicious Falcon schrieb:
PauleJS schrieb:
error C2512: 'std::vector' : no appropriate default constructor available
vliegreizen.h(11) : error C2065: 'string' : undeclared identifier
vliegreizen.h(11) : error C2065: 'string' : undeclared identifierSchau dir einmal diese 3 Fehlermeldungen an. C2065 bedeutet : "Der angegebene Bezeichner wurde nicht deklariert".
Du includierst zwar<vector>, jedoch nicht<string>. Da du außerdem vernünftigerweise keinusing namespace std;in die Header-Datei geschrieben hast, sollte es anschließend auchstd::vector<std::string> data;heißen.o man ich brauche noch viel übgung in c++^^ hast total recht gehabt und nun arbeitet es fehlerfrei
besten dank!
-
will jetzt ab zeile 1 in einem abstand von jeweils 5 zeilen immer eine neue zeile mit einem int wert hinzufügen! bekomme aber immer fehlermeldungen, oder die schleife ist unendlich lang.
lese also folgende datei ein:
Andreas Müller 95 1 Fussball Verein HSV 8763 34 Kegelclub SIXT 1264 15dazu dieser code
#include <fstream> #include <string> #include <vector> #include <iostream> using namespace std; void main (void){ string test = "test"; vector<string> data; ifstream readFile( "inFile.txt" ); char zeile [30]; // max 30 characters while(readFile.getline (zeile, 500)){ data.push_back(zeile); } vector<string>::iterator pos; data.at(1) = data.push_back(test); //beginnend von zeile 1 soll in abstand von 5 zeilen eine neue zeile eingefügt werden for (pos = data.begin(); pos != data.end(); ++pos){ cout << *pos << endl; } readFile.close(); }dieses beispiel soll jetzt erstmal nur einen string einfügen. später soll aber eine int eingefügt werden, denke dieser muss dann erst in einen string konvertiert, oder nicht?
besten dank im voraus für die hilfe!!
-
PauleJS schrieb:
// ... char zeile [30]; // max 30 characters while(readFile.getline (zeile, 500)){Hallo PauleJs,
wieso dieser Code - wo lernt man sowas? Niemand hier hat Dir geraten, char-Arrays zu benutzen. Man macht nur Fehler - so wie Du in diesem Fall.
Wir haben hier gearde einen ellenlangen Thread zum Thema string versus char[].
ratlos

Werner