Daten aus Textdokument auslesen und in vector schreiben



  • 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 gelesen
    

    Jetzt 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


  • Mod

    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
    34
    

    Die 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::ws
    

    in 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!


  • Mod

    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


  • Mod

    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 illegal

    Das 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 identifier

    header 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 identifier

    Schau 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 kein using namespace std; in die Header-Datei geschrieben hast, sollte es anschließend auch std::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 identifier

    Schau 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 kein using namespace std; in die Header-Datei geschrieben hast, sollte es anschließend auch std::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
    15
    

    dazu 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


Anmelden zum Antworten