Objekte in Datei schreiben und lesen!



  • Hallo,
    habe ein kleines Programm das mehrere Namen als einfach verkettete Liste einlesen und ausgeben kann. Jede Person bzw. jeder Name ist ein Objekt (siehe Code).
    Mein Ziel ist es die Eingaben gleichzeitig in eine Datei zu schreiben. Wenn ich das Programm das nächste mal starte, sollen automatisch alle bisher eingegeben Namen aus der Datei eingelesen werden und die Liste am letzten Namen fortgesetzt werden usw. !
    Mich würde es wirklich sehr freuen wenn Ihr mir die entsprechend benötigten Befehle in meinen Code eintragen würdet.
    Bitte habt Rücksicht da ich noch C++ Anfänger bin und sehr geren anhand von Beispielen lerne.

    Vielen Dank im vorraus an alle Helfer.
    MfG Annika.

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Person{
    private:
    	string name;
    	Person *next;
    public:
    	void setDaten(Person **liste_pointer);
    	void getDaten();
    };
    
    void Person::setDaten(Person **liste_pointer){
    
    	if(NULL==(*liste_pointer)){										
    		*liste_pointer=new Person;								
    		if(NULL==(*liste_pointer)){									
    			cout<<"\nNicht genug Speicher vorhanden" <<endl;
    			exit(0);												
    		}
    		cout<<"\nName: ";
    		cin>>(*liste_pointer)->name;							
    		(*liste_pointer)->next=NULL;								
    
    	}	
    	else{	
        setDaten(&((*liste_pointer)->next));	
              } 		
    }
    
    void Person::getDaten(){
    
    	if(this!=NULL){										
    		cout<<"\n\nName: " <<name;
    	         next->getDaten();					
    	}
    }
    
    int main(){
    
    	Person *wurzel=NULL;
    
    	int auswahl=0;
    
    	for(;;){
    
    	cout<<"\n\n1) Name eingeben" <<endl;
    	cout<<"2) Alle Namen ausgeben" <<endl;
    	cout<<"\nAuswahl: ";
    	cin>>auswahl;
    	fflush(stdin);
    
    	if(auswahl==1){						
    		wurzel->setDaten(&wurzel);			
    		auswahl=0;	
    	}
    
    	if(auswahl==2){
    		wurzel->getDaten();				
    	}
    
    	}
    	return 0;
    
    }
    


  • Z.B. So:

    #include <iostream>
    #include <fstream>
    #include <string>
    using namespace std;
    
    const string tokens(";");        //erweiterbar...(*,:,...)
    
    class Personenliste{
    private:
        struct Listenelement
        {
            string name;
            *Listenelement next;
        };
        Listenelement first;
    public:
        Person(const string& Quelle="personenenliste.txt");    //Konstruktor
        ~Person();                                             //Destruktor
        void safe(const string& Ziel="personenenliste.txt")
    };
    
    Person::Person(const string& Quelle)
    {
        istream quelle;    //Neuen "Eingabedateistrom" anlegen
        quelle.open(Quelle.c_str(),ios::binary|ios::in);    //Öffnen, zum auslesen
        if(!quelle)
            cerr<<Quelle<<" konnte nicht geöffnet werden!";
        else
        {
            string ziel;
            quelle>>ziel;    //Ziel enthält Inhalt der Datei
            first.name=ziel.substr(0,ziel.find_first_of(tokens)+1);
            ziel.erase(0,ziel.find_first_of(tokens)+1);
            Listenelement* now;
            for(size_type p=first.find_first_off(tokens)+1,now=&first;p!=string::npos;p=ziel.find_first_off(tokens)+1)
            {
                now->next=new Listenelement;
                now=now->next;
                now->name=ziel.substr(0,p);
                ziel.erase(0,p);
            };
            now->next=0;
        };
        quelle.close();    //Wird von Destruktor erledigt, kann net schaden
    };
    
    Person::safe(const string& Ziel)
    {
        Listenelement* now=&first;
        ofstream ziel;
        ziel.open(Ziel.c_str(),ios::binary|ios::out);    //Zum Schreiben öffnen
        if(!ziel)
            cerr<<Ziel<<" konnte nich geöffnet werden!";
        else
            for(now,now->next!=0;now=now->next)
                ziel<<now->name;                             //Richtig??
    };
    

    Ich hab das ein bisschen geändert, erscheint mir sinnvoller so.
    Ich würde noch eine Funktion zum Hinzufügen einer Person vorschlagen, außerdem ein Speichern im Destruktor. Außerdem musst du im Destruktor die Liste Löschen, oder du benutzt smartpointer...
    Achja: Ich hab das hier so hingetippt, wer weiß wieviele Fehler da drin sind.



  • Erstmal Danke.

    Uff ganz schön viele neue Sachen...! Vom Prinzip her dachte ich eigentlich das mein eigener Code erhalten bleibt und man das Speichern/Laden irgendwie da eintragen könnte bzw. integriert? Oder ist mein Code nicht brauchbar in dieser Hinsicht? Sorry wenn ich hier was falsch verstehe, aber ich betrete hier absolutes Neuland.



  • HALT!

    Dafür gibt es doch extra das Stream Konzept in C++, das erlaubt es einen, dann eben Objekte auf allen möglichen Streams auszugeben (Dateien, Konsolen, Netzwerk etc.)

    #include <iostream>
    #include <fstream>
    #include <string>
    
    class Personen {
      std::string name;
      unsigned short alter;
      //...
    public:
      Personen() { }
      Personen(std::string const &n,unsigned short a)
        : name(n),alter(a)
      { }
    
      friend std::ostream &operator<<(std::ostream &out,Personen const &p) {
        out << p.name << '\n' << p.alter;
        return out;
      }
      friend std::istream &operator>>(std::istream &in,Personen &p) {
        std::getline(in,p.name);
        in >> p.alter;
        return in;
      }
    };
    
    void foo() {
      Personen a("Michael",12);
      Personen b("Ulf",8);
      std::cout << a << '\n' << b << '\n';
    
      std::ofstream out("datei.txt");
      out << a << b;
    }
    
    void bar() {
      Personen a;
      Personen b;
      std::ifstream in("datei.txt");
      in >> a >> b;
    
      std::cout << a << '\n' << b <<'\n'; 
    }
    
    int main() {
      foo();
      bar();
    }
    




  • Ja, die Sache mit dem Eintragen ist das Problem, mir ist deine Klasse zu umständlich. ABer in Kürze:

    #include <fstream>                                 //Arbeit mit Ströhen aus/in Dateien
    #include <iostream>
    using namespace std;
    
    int main()
    {
        ofstream ziel;                                 //Ausgabestrom mit Namen ziel
        ziel.open("ziel.txt",ios::out|ios::binary);    //Datei ziel.txt öffnen, wir wollen hinein schreiben und wir wollen den "Text"
                                                       //der Datei haben (Wenn es z.B. ein  Programm wäre)
        if(!ziel)                                      //Mit dem überladenen Operator ! prüfen wir, ob die Datei geöffnet/erstellt werden konnte
            cerr<<"Fehler!!!";                         //cerr=Fehlerkanal
        else
            ziel<<"Das ist ein Test!";                 //Wie mit cout, nur in die Datei und nicht in die Ausgabe
        ziel.close();                                  //Schließen, sonst können wir die Datei nicht nochmal öffen
        string einlesen("Fehler!");
        ifstream quelle;
        quelle.open("ziel.txt",ios::in|ios::binary);   //Zum Einlesen öffen
        if(!quelle)
            cerr<<"Fehler!!!";
        else
            quelle>>einlesen;
        cout<<einlesen;                                //Hoffentlich: "Das ist ein Test!"
        cin.get();
        //quelle.close();                              //Macht der Destruktor für uns
    };
    


  • @Kingruedi:
    Und was machst du jetzt, was ich nicht mache? Sie(?) wollte schließlich die ganze sache Abspeichern und dann wieder lesen, das hab ich halt versucht...
    Aber einen überladenen operator{<<,>>} zu definieren ist natürlich ein guter Schachzug. Was aber bewirkt sowas:

    friend std::ostream &operat...
    

    ? Ich glaube du erlaubst gleichzeitig ostream auf deine Daten zuzugreifen und sagst, das du einen ostream& zurückgibst.



  • Also Grundsätzlich hab ich das ja verstanden, aber bekomm ich das überhaupt in meine verkettete Liste rein ? Das speichern kann ich mir noch gut vorstellen, aber wie siehts mit dem einlesen aus wenn das Programm gestartet wird ? Immerhin soll man danach beim letzten eingelesen Namen weitermachen können! Gar nicht so einfach...!



  • ness schrieb:

    @Kingruedi:
    Und was machst du jetzt, was ich nicht mache? Sie(?) wollte schließlich die ganze sache Abspeichern und dann wieder lesen, das hab ich halt versucht...
    Aber einen überladenen operator{<<,>>} zu definieren ist natürlich ein guter Schachzug. Was aber bewirkt sowas:

    friend std::ostream &operat...
    

    ? Ich glaube du erlaubst gleichzeitig ostream auf deine Daten zuzugreifen und sagst, das du einen ostream& zurückgibst.

    Ja, das muss auch so sein. Denn ohne friend, koennte er nicht die Daten ausgeben
    und die <</>>-Ops koennen nicht als Memberfunction implementiert werden. Ein
    ostream& muss man zurueckgeben, um das nicht neuschreiben zu
    muessen, gebe ich hier mal ein Link zu Volkards aelterem Kurs, wo das sehr gut
    erklaert ist:

    http://www.volkard.de/vcppkold/operatoren_ausserhalb_von_klassen.html

    mfg
    v R



  • @ness
    siehe http://www.cpp-tutor.de/cpp/le12/le12_05.htm

    [EDIT] vieeeeeeeelllll zu langsam 😃



  • OK also das schreiben in eine Datei klappt jetzt mit meinem Code. Blos weiß ich einfach nicht wie ich den Kram einlesen kann ? Das ganze müsste ja ganz am Anfang von Main eingelesen und in die verkettete Liste eingetragen werden. Aber wie ??? Wenn man dann einen neuen User einträgt soll die Liste quasi fortgeführt werden.

    Hier mein veränderter Code mit der Datei schreibe Funktion:

    #include <iostream> 
    #include <string> 
    #include <fstream>
    using namespace std; 
    
    class Person{ 
    private: 
        string name; 
        Person *next; 
    public: 
        void setDaten(Person **liste_pointer); 
        void getDaten(); 
    
    	friend std::ostream &operator<<(std::ostream &out,Person const &p) { 
        out << p.name << '\n'; 
        return out; 
      } 
        friend std::istream &operator>>(std::istream &in,Person &p) { 
        std::getline(in,p.name); 
        return in; 
      } 
    }; 
    
    void Person::setDaten(Person **liste_pointer){ 
    
        if(NULL==(*liste_pointer)){                                        
            *liste_pointer=new Person;       
    
            cout<<"\nName: "; 
            cin>>(*liste_pointer)->name;                            
            (*liste_pointer)->next=NULL;   
    
    		std::ofstream out("datei.txt"); 
    		out << **liste_pointer;      
        }    
        else{    
        setDaten(&((*liste_pointer)->next));    
              }         
    } 
    
    void Person::getDaten(){ 
    
        if(this!=NULL){                                        
            cout<<"\n\nName: " <<name; 
                 next->getDaten();                    
        } 
    } 
    
    int main(){ 
    
        Person *wurzel=NULL; 
    
        int auswahl=0; 
    
        for(;;){ 
    
        cout<<"\n\n1) Name eingeben" <<endl; 
        cout<<"2) Alle Namen ausgeben" <<endl; 
        cout<<"\nAuswahl: "; 
        cin>>auswahl; 
        fflush(stdin); 
    
        if(auswahl==1){                        
            wurzel->setDaten(&wurzel);            
            auswahl=0;    
        } 
    
        if(auswahl==2){ 
            wurzel->getDaten();                
        } 
    
        } 
        return 0; 
    
    }
    

    Danke Leute!



  • Nachtrag:

    Pustekuchen! Ich seh ja eben erst das der immer nur den letzten Namen in meine Datei schreibt. !??? Jetzt bin ich verwirrt.



  • friend std::ostream &operator<<(std::ostream &out,Personen const &p)
    

    asche über das Haupt desjenigen, der kurz vorher noch das gesagt hat:

    Dafür gibt es doch extra das Stream Konzept in C++, das erlaubt es einen, dann eben Objekte auf allen möglichen Streams auszugeben (Dateien, Konsolen, Netzwerk etc.)

    das wiederspricht sich nämlich, da ostream schon ein typedef auf basic_ostream<char,char_traits<char> > ist, und man so a( nicht mit neuen netzwerkresourcen arbeiten könnte, und man b) alle w- versionen vergessen könnte.

    richtig wärs wohl so:

    template<class charT,class traits>
    friend std::basic_ostream<charT,traits> &operator<<(std::basic_ostream<charT,traits> &out,Personen const &p)
    

    sorry, king, darauf musste ich noch hinweisen :), kannst dich ja bald mal revanchieren 😃

    //edit ist zwar in diesem fall nicht ganz akut, aber man weis ja nie, wie elemente dritter implementiert sind, und ob man das ganze nicht auch mal über wcout ausgeben können will 🙂



  • @vr: Was du sagst ist mir bekannt, eogentlich wollte ich nur wissen was das "HALT" sollte? Ich _habe_ doch die tollen Ströme benutuzt, auch wenn ich << und >> nicht überladen habe.
    @Annika: Versuche mal die Klasse etwas umzubauen, so das sie einfacher wird. So das sie Liste sich selber vrwaltet, und du nicht Wurzel, ... speichern musst sondern alles aufeinmal in der Liste. Mein Beispiel oder auch std::list (kompliziert) könnte dir ein Bild geben.



  • @Ness

    Ich muss es leider so machen. Ist ne Bedingung der Aufgabe das ich meine Liste so aufbau. Leider... 😞



  • OK jetzt weiss ich warum immer nur 1 Name in der Datei steht, weil sie immer wieder neu angelegt wird, denn die Methode wird ja auch mehrmals angelegt. Definier ich dieses:
    std::ofstream out("datei.txt");

    außerhalb der Methode schreibt er komischerweise gar nichts mehr in die Datei. Komisch...!



  • Nabend,

    sorry, king, darauf musste ich noch hinweisen :), kannst dich ja bald mal revanchieren 😃

    Warum musst du immer so unverschaemt sein? 😃

    @vr: Was du sagst ist mir bekannt, eogentlich wollte ich nur wissen was das "HALT" sollte? Ich _habe_ doch die tollen Ströme benutuzt, auch wenn ich << und >> nicht überladen habe.

    Ups, sry. Da hab ich dich glaube ich falsch verstanden :). So ein 'halt'
    rutscht mir manchmal auch raus, das ist mit Sicherheit nicht persoenlich
    gemeint gewesen. Er wollte lediglich darauf hinweisen, dass man hier
    prima den Op << und >> auf otzes Art und Weise (jaja ich meine das so :D)
    ueberladen kann.

    Zum Thema Listen. Eine einfach verkettete Liste implementiert man i.d.R so:

    class Person{
    private:
        string name;
        Person *next;
    public:
        Person() 
            : next(0) //0 makiert das ende
        {}
        //ich versteh nicht ganz, warum hier Person**? 
        //void setDaten(Person **liste_pointer);
        //so ganz spontan haette ich das so gemacht:
        void setDaten(const Person& p);
        void getDaten();
    
        friend std::ostream &operator<<(std::ostream &out,Person const &p) {
        out << p.name << '\n';
        return out;
      }
        friend std::istream &operator>>(std::istream &in,Person &p) {
        std::getline(in,p.name);
        return in;
      }
    };
    
    void Person::setDaten(const Person& p){
        Person* tmp = this; //this ist kopf der liste
        Person* newPerson = new Person;
    
            //daten kopieren
            newPerson->name = p.name;
    
            //an das ende der liste laufen
            while(tmp)
                tmp = tmp->next;
    
            //wir sind am ende angelangt, newPerson ist das neue
            //letzte element
            tmp = newPerson;
            tmp->next = 0; //damit o. schleife auch funktioniert
    }
    
    //die funktionalitaet sollte jetzt klar sein
    void Person::getDaten(){
        Person* tmp = this;
            while(tmp) {
                cout<<name<<endl;
                tmp = tmp-> next;
            }
    }
    

    Hoffe ich hab jetzt keinen Fehler gemacht und dir ist das Prinzip klar geworden.

    mfg
    v R



  • Wie rufe ich diese Liste denn in MAIN auf und wo gebe ich den Namen ein ?
    Das Speichern/Lesen ist ja noch nicht integriert, mal abgesehen von den Funktionen. Hast du da noch ne Idee ?



  • Hä? Sicher das amn so eine Liste implementiert? Also vom Prinzip her schon, aber ich kann ja nicht mal nen name speichern? Oder bin ich blöd?
    @Annika:
    Speichern ist von einzelnen Personen ist implementiert, und das ist nur ein Bsp. Wenn du sowas benutzen willst, kannste std::list benutzen. Wenn dus selber implementieren willst (zu übungszwecken) musst du das natürlich noch erweitern.
    Mir sagt übrigens die ganze Listenklasse nicht zu und mindestens einen Destruktor würde ich noch vorschlagen!



  • ness schrieb:

    Hä? Sicher das amn so eine Liste implementiert? Also vom Prinzip her schon, aber ich kann ja nicht mal nen name speichern? Oder bin ich blöd?

    Ups, hab en Fehler drin. Bei setDaten tmp = next setzen. Und ja, en DTor sollte
    noch rein, um die Liste wieder zu loeschen.

    Bei mir wird die Liste ordnungsgemaess angelegt.

    [edit]
    Die Schleife in setData muss noch raus
    [/edit]

    mfg
    v R


Anmelden zum Antworten