Aus .txt Datei lesen



  • Ist ja was, was immer passieren kann 🤡

    Ich frage mich gerade nur, wieso fstream überhaupt einen << Operator bekommen hat.

    Oder warum liefert die Ausgabe keinen Compilerfehler?
    Ist es vielleicht die implizierte Umwandlung in void* um ein überprüfen im Sinne von if(file == NULL) etc. machen zu können?



  • Noch eine Frage zu meinem Code, haltet ihr es für sinvoll wenn ich in der Methode "describe" und "readout" mit der Elementfunktion "error()" [die ja in beiden Klassen vorhanden ist] die Richtigkeit überprüfe, obs Komplikationen,Fehler etc. gab. Oder soll ich einfach davon ausgehen, das der Nutzer, das selbst übernimmt, weil "error()" ist ja public.
    Was meint ihr dazu, wie würdet ihr das machen, ist es sinnvoll da extra nochmal zu prüfen?
    MfG
    Stromberg



  • Also ich mein den Code von meinem ersten posteing, wenn jemand jetzt nich weiß was gemeint is.



  • Geil! 😃 describe = einen Gegenstand oder ein Sache Beschreiben im Sinne von jmd. sagen wie es aussieht. Nicht im sinne von eine CD mit Daten beschreiben 😉

    Aja ehm warum macht ihr eure Klassen denn immer abhängig von einem bestimmten Ein oder Ausgabestream? Warum lasst ihr nicht generell alle Ein-/Ausgabestreams zu? (std::ostream statt std::cout u. std::istream statt std::cin).

    Und sowas:

    bool write::error()
    {
        if(m_file) return false;
        return true;
    }
    

    ...

    bool write::error() const
    { return !m_file; }
    

    Man beachte das const und die Kürzung der ganzen return's.

    Wenn du Zeilenweise schreibst, musst du auch zeilenweise lesen! Und std::string wird per default-Konstruktor mit "" initialisiert. Das muss man nicht mehr machen ...

    bool read::show(std::ostream& out)
    {
        if (error()) return false;
    
        std::string buff;
        while (std::getline(m_file, buff))
            out << buff << std::endl;
    
        return m_file;
    }
    

    Wenn in der Datei ein Endline-Zeichen ist, solltest du es auch bei der Ausgabe mit ausgeben, um die äquvalenz zwischen Datei & Ausgabe bei zu behalten 😉



  • Ach so, ist die Zeile hier:

    { return !m_file; }
    

    eine Kurzform für das:

    bool write::error()
    {
        if(m_file) return false;
        return true;
    }
    

    ...cool, wusst ich net.
    Jo, und ich sollte natürlich viel mehr const bei den Methoden verwenden.
    Aber eigentlich bräuchte es des doch gar net mit dem nochmaligen überprüfen mit "error()" in der Methode "show", weil ich kann doch auch einfach dafür ausgehen, dass das der Nutzer zuvor selber macht?

    PS: Zum "describe", da sieht man mal wieder wieviel des Schulenglisch bringt, und wie ich einfach nur stur und ohne sinn und verstand meine Vokabeln auswendig lerne^^

    MfG
    Stromberg



  • Ach so, ist die Zeile hier:

    { return !m_file; }
    

    eine Kurzform für das:

    bool write::error()
    {
        if(m_file) return false;
        return true;
    }
    

    ...cool, wusst ich net.

    Hmm nach den De Morgan'sche Gesetzen sollte das so sein, ja. Allerdings ist es leider(meistens) auch in C++ möglich bsw. operator! und operator void* zu überladen, sodass z.B. if (m_file) und if (!m_file) beide gleichzeitig wahr sind.

    Aber eigentlich bräuchte es des doch gar net mit dem nochmaligen überprüfen mit "error()" in der Methode "show", weil ich kann doch auch einfach dafür ausgehen, dass das der Nutzer zuvor selber macht?

    Hmm natürlich kannst du davon ausgehen. Man kann von vielem ausgehen, doch man sich auch vergewissern. Das hängt ganz davon ab, was du willst 😉



  • Jetzt hab ich da mal n bissel im Code umgedingst, hier und da mal n "const" gesetzt, aber jetzt spackt der Compiler rum:

    #include <iostream>
    #include <fstream>
    #include <string>
    using namespace std;
    
    class read
    {
        public:
        read(const string &file_name);
        inline bool error() const;
        bool readout() const;
    
        private:
        const ifstream m_file;
    };
    
    read::read(const string &file_name)
    :m_file(file_name.c_str())
    {
    
    }
    
    bool read::error() const
    {
        return !m_file;
    }
    
    bool read::readout() const
    {
        if(error()) return false;
        string buffer;
        while(getline(m_file,buffer))
        {
            cout << buffer << "\n";
        }
        return m_file;
    }
    
    class write
    {
        public:
        write(const string &file_name);
        inline bool error() const;
        bool fill_in();
    
        private:
        ofstream m_file;
    
    };
    
    write::write(const string &file_name)
    :m_file(file_name.c_str())
    {
    
    }
    
    bool write::error() const
    {
        return !m_file;
    }
    
    bool write::fill_in()
    {
        if(error()) return false;
        string buffer;
        while(getline(cin,buffer))
        {
            if(buffer == "exit") return true;
            m_file << buffer << "\n";
        }
        return m_file;
    }
    
    int main()
    {
    
        return 0;
    }
    

    Compiling: C:\MinGW\Andi\text_edit.cpp
    C:\MinGW\Andi\text_edit.cpp: In member function bool read::readout() const': C:\\MinGW\\Andi\\text_edit.cpp:32: error: no matching function for call togetline(const std::ifstream&, std::string&)'
    Process terminated with status 1 (0 minutes, 0 seconds)
    1 errors, 0 warnings

    Pourqoui? Ich verstehs nicht, irgendwas passt ihm am "const" net, hoff mal dass es nicht wieder n dummer Leichtsinnsfehler von mir is.

    MfG
    Stromberg



  • durch das const werden alle membervariablen (in der jeweiligen methode) konstant (außer jene, die als mutable spezifiziert worden sind).
    getline erwartet allerdings als übergabeparameter eine referenz auf einen nicht-konstanten istream.

    vielleicht auch ein hinweis, die ifstream klasse nicht zu einem klassenmember zu machen 😉



  • Ja, mhm, aber es ist doch schon cooler und vom Stil besser, wenn ich die ifstream Klasse als Klassenmember mache. Wo soll ich die den sonst hin tun? Ich kann das ding ja nicht einfach "global" machen, wenn der Code mal noch wächst....is das doch schon mal n schlechter Untergrund?!
    Ich hätt das jetzt so gemacht:

    #include <iostream>
    #include <fstream>
    #include <string>
    using namespace std;
    
    class read
    {
        public:
        read(const string &file_name);
        inline bool error() const;
        bool readout() const;
    
        private:
        const ifstream m_file;
    };
    
    read::read(const string &file_name)
    :m_file(file_name.c_str())
    {
    
    }
    
    bool read::error() const
    {
        return !m_file;
    }
    
    bool read::readout() const
    {
        if(error()) return false;
        string buffer;
        const ifstream *temp_const=&m_file;
        ifstream *temp=const_cast<ifstream*>(temp_const);
        while(getline(*temp,buffer))
        {
            cout << buffer << "\n";
        }
        return m_file;
    }
    
    class write
    {
        public:
        write(const string &file_name);
        inline bool error() const;
        bool fill_in();
    
        private:
        ofstream m_file;
    
    };
    
    write::write(const string &file_name)
    :m_file(file_name.c_str())
    {
    
    }
    
    bool write::error() const
    {
        return !m_file;
    }
    
    bool write::fill_in()
    {
        if(error()) return false;
        string buffer;
        while(getline(cin,buffer))
        {
            if(buffer == "exit") return true;
            m_file << buffer << "\n";
        }
        return m_file;
    }
    
    int main()
    {
        read test("D:/test.txt");
        test.readout();
        return 0;
    }
    

    Ist "const_cast" hier fehl am Platz, weil dass soll ja auch schlechter Stil sein?
    Was schlagt ihr mir dann vor, wie mach ich dass dann am besten?
    Warum muss den bitte des blöde "getline" eine Referenz haben die nur nicht konstante Objekte nimmt?!
    Ma so nebenbei, wie mach ich des bitte, das ein Parameter konstante Objekte nicht annimmt?
    Mh ich könnte jetzt natürlich die "readout" Methode nicht const deklarieren, aber is ja auch schlechter Stil....was tun?!

    MfG
    Stromberg



  • Stromberg schrieb:

    Ist "const_cast" hier fehl am Platz, weil dass soll ja auch schlechter Stil sein?
    Was schlagt ihr mir dann vor, wie mach ich dass dann am besten?
    Warum muss den bitte des blöde "getline" eine Referenz haben die nur nicht konstante Objekte nimmt?!

    weil getline aus dem stream etwas ausliest und ihn daher verändert. der stream wird verändert, also darf er nicht konstant sein. ist doch ganz logisch 😉
    const_cast ist hier nicht nur schlechter stil, sondern provoziert undefiniertes verhalten, denn dein stream ist "tatsächlich" konstant. ich sage dir jetzt mal nicht die schuss-in-den-fuß lösung mit mutable, sondern rate dir dazu, den stream lokal in der funktion zu halten und dir stets genau zu überlegen, ob eine methode ihr objekt verändert oder nicht. wenn ja, dann darf sie eben nicht const sein.

    Ma so nebenbei, wie mach ich des bitte, das ein Parameter konstante Objekte nicht annimmt?

    du überlädst die funktion für konstante objekte und lässt sie undefiniert (wenn ich richtig verstanden habe, was du willst)

    Mh ich könnte jetzt natürlich die "readout" Methode nicht const deklarieren, aber is ja auch schlechter Stil....was tun?!

    const macht noch keinen guten stil. wie gesagt, es hängt von der semantik der methode ab. wenn sie was ändert, dann natürlich nicht const.



  • Was bedeutet den genau "undefiniertes Verhalten"?
    Es wäre aber doch auch nicht extrem schlechter Stil wenn ich die "readout" Methode einfach nicht const machen würde?
    Oder ich machs so wie dus gesagt hast:

    bool read::readout() const
    {
        ifstream file(m_file_name.c_str());
        if(!file) return false;
        string buffer;
        while(getline(file,buffer))
        {
            cout << buffer << "\n";
        }
        return file;
    }
    

    Is beser so oda?
    MfG
    Stromberg



  • Mh was ist eigentlich an "mutable" so schlecht, fällt mir grad auf???!
    MfG
    Stromberg



  • Noch was, "(D)Evil" schrieb:

    bool read::show(std::ostream& out) 
    { 
        if (error()) return false; 
    
        std::string buff; 
        while (std::getline(m_file, buff)) 
            out << buff << std::endl; 
    
        return m_file; 
    }
    

    dass müsste man dann doch wohl so anwenden oder:

    ....
    read test;
    test.show(std::cout);
    ....
    

    Und bei meiner Methode befindet sich das "cout" halt gleich in der Methode, also:

    ....
    cout << buf << "\n";
    ....
    

    Was ist jetzt besser\schneller? Oder ist es egal?

    MfG
    Stromberg



  • Hmm der Unterschied besteht viel mehr in der vielseitigeren Verwendungsmöglichkeit. Du kannst deine Ausgabe nur in std::cout Umleiten. Durch die Übergabe des Ausgabestreams an die Funktion kannst du bsw. auch in eine andere Datei umleiten.



  • (D)Evil schrieb:

    Hmm der Unterschied besteht viel mehr in der vielseitigeren Verwendungsmöglichkeit. Du kannst deine Ausgabe nur in std::cout Umleiten. Durch die Übergabe des Ausgabestreams an die Funktion kannst du bsw. auch in eine andere Datei umleiten.

    du kannst cout auch in eine datei umleiten, indem du dessen streambuf änderst 😉
    aber das wäre natürlich nicht so.. hm.. intuitiv.
    intuitiv wäre ein operator<< wohl am ehesten. aber das schlägt sich etwas mit dem konzept, das hinter dieser klasse zu stehen scheint. aber ob es so gut ist, ein objekt zu haben, dass mit dem namen "read" eher an eine funktion erinnert?

    Stromberg schrieb:

    Was bedeutet den genau "undefiniertes Verhalten"?

    darüber bist du noch nicht gestoßen? das bedeutet, dass dein compiler aus dem code alles mögliche machen kann. im schlimmsten fall lässt es kleine dämonen aus deiner nase springen.

    an mutable ist grundsätzlich nichts schlechtes (wenn man es richtig verwendet), aber du deklarierst deine funktion als "funktion, die nichts an der jeweiligen instanz ändert" und dann willst du deine membervariablen so spezifizieren (mit mutable), dass sie trotzdem geändert werden können? hebt die wirkung auf. verzichte auf const. (oder lagere den stream aus, was zusätzlich den vorteil bringt, dass deine klasse kopierbar wird.)



  • Hier is der Code nochma, bissel umgeändert, aufgeteilt und noch bissel verpackt....
    main.cpp

    #include "text_editor.hpp"
    
    int main()
    {
        text_editor program;
        while(program.menu());
    
        return 0;
    }
    

    text_editor.hpp

    #ifndef TEXT_EDITOR_HPP_INCLUDED
    #define TEXT_EDITOR_HPP_INCLUDED
    
    #include <iostream>
    #include <string>
    
    class read
    {
        public:
        read(const std::string &file_name_);
        bool readout(std::ostream &ios_object) const;
    
        private:
        const std::string m_file_name;
    };
    
    class write
    {
        public:
        write(const std::string &file_name_);
        bool fill_in(bool remove);
    
        private:
        const std::string m_file_name;
    
    };
    
    //Macht die Klassen "read" und "write" Benutzerfreundlicher
    class text_editor
    {
        public:
        text_editor();
        bool menu();
        bool handle_file_write() const;
        bool handle_file_read() const;
        void error_message(const std::string &message);
    
        private:
        bool m_remove;
    
    };
    
    #endif // TEXT_EDITOR_HPP_INCLUDED
    

    text_editor.cpp

    #include <iostream>
    #include <fstream>
    #include <conio.h>
    #include "text_editor.hpp"
    #include "ic.hpp" //Improved Konsole
    
    using namespace std;
    using namespace ic; //Improved Konsole
    
    //KLASSE READ
    read::read(const std::string &file_name_)
    :m_file_name(file_name_)
    {
    
    }
    
    //Methode um Datein auszulesen
    bool read::readout(ostream &ios_object) const
    {
        ifstream file(m_file_name.c_str()); //<-- Streamobjekt "file"
        if(!file) return false; //Stream fehlerhaft?
        if(!ios_object) return false; //Objekt Fehlerhaft?
        string buffer;
        while(getline(file,buffer))
        {
            ios_object << buffer << "\n";
        }
        //<-- Ab hier wird "file" false!!! Warum?
        file.clear(); //Kann das nur mit dem hier beheben, sonst wird text_editor::message_error() aufgerufen
                      //Aber ist ja auch bissel gemogelt wenn ich dann einfach alles wieder "clear()" mache.
                      //Aber was hat "file" hier für ein Problem?
        cout << file << endl;
        getch();
        return file;
    }
    
    //KLASSE WRITE
    write::write(const std::string &file_name_)
    :m_file_name(file_name_)
    {
    
    }
    
    bool write::fill_in(bool remove)
    {
        ofstream file;
        if(remove) file.open(m_file_name.c_str()); //Datei wird immer neu erstellt
        else file.open(m_file_name.c_str(), ios::out | ios::app); //In Datei weiter schreiben
    
        if (!file) return false;
        string buffer;
        while(getline(cin,buffer))
        {
            if(buffer == "exit") return true;
            file << buffer << "\n";
        }
        return file;
    }
    
    //KLASSE TEXT_EDITOR
    text_editor::text_editor()
    :m_remove(false)
    {
    }
    
    bool text_editor::menu()
    {
        short input;
        cout << "< 1 > Datei beschreiben\n";
        cout << "< 2 > Datei lesen\n";
        cout << "< 3 > Programm beenden\n";
    
        cout << "\n\n\n### OPTIONEN ###\n\n";
        cout << "< 4 > Beim beschreiben Datei immer neuerstellen";
        if(m_remove) cout << " 'AN'\n";
        else cout << " 'AUS'\n";
    
        cout << "\n\n\n--> ";
        cin >> input;
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(),'\n');
    
        //Bildschirm löschen | Cursor Poition nach oben
        con.clearColor(BG_BLACK);
        con.setCurPos(0,0);
    
        switch(input)
        {
            case 1:
            if(!handle_file_write()) error_message("Das Beschreiben musste beendet werden.\n");
            break;
            case 2:
            if(!handle_file_read()) error_message("Das Lesen musste beendet werden.\n") ;
            break;
            case 3:
            return false;
            break;
            case 4:
            if(m_remove) m_remove=false;
            else m_remove=true;
            break;
        }
    
        //Bildschirm löschen | Cursor Poition nach oben
        con.clearColor(BG_BLACK);
        con.setCurPos(0,0);
    
        return true;
    }
    
    bool text_editor::handle_file_write() const
    {
        string save_name;
        cout << "Dateiname: ";
        getline(cin,save_name);
        cout << "'exit' am Zeilenanfang + 'ENTER' = Schreibprogramm wird beendet\n\n";
        write t_e_write(save_name);
        return t_e_write.fill_in(m_remove);
    }
    
    bool text_editor::handle_file_read() const
    {
        string save_name;
        cout << "Dateiname: ";
        getline(cin,save_name);
        cout << "Beliebiger Tastendruck = Leseprogramm wird beendet\n\n";
        read t_e_read(save_name);
        return t_e_read.readout(cout);
    }
    
    //Fehlermeldung, wenn ein stream "false" zurückgibt
    void text_editor::error_message(const string &message)
    {
        //Bildschirm löschen | Cursor Poition nach oben
        con.clearColor(BG_BLACK);
        con.setCurPos(0,0);
    
        cout << "Ein Problem wurde festgestellt!\n";
        cout << message;
        getch();
    }
    

    Was haltet ihr davon?

    Hab noch ein Problem, meine Methode "readout" gibt immer "false" zurück, wodurch ein Error aufgerufen wird. Das liegt daran dass das Streamobjekt "file" plötzlich nach der while Schleife false ist, obwohls vorher noch true war....
    [siehe: text_editor.cpp, Zeile 29]
    Die Datei wird super eingelesen, alles...richtig aufm Bildschirm der Text ausgegeben...passt alles, nur das "file" dann "false" ist, und dadurch "false" von der Methode "readout" zurück gegeben wird, und dadurch wird wiederum die "Error Methode" aufgerufen!!!! Ich versteh aber einfach nicht warum diese verdammte Streamobjekt "false" wird?!!!
    Vll. blickt da eienr von euch besser durch.

    MfG
    Stromberg



  • Du liest in readout solange mit getline ein bis die false zurückliefert. Dies ist dann der Fall wenn nichts mehr gelesen werden kann, also am Dateiende. Da wird aber dann auch eof() gesetzt und file liefert somit false zurück.
    Die Lösung mit clear ist da schon ok evtl. kannst du ja noch den Filepointer auf den Anfang zurück setzten.



  • bool write::fill_in(bool remove)
    {
        ofstream file;
        if(remove) file.open(m_file_name.c_str()); //Datei wird immer neu erstellt
        else file.open(m_file_name.c_str(), ios::out | ios::app); //In Datei weiter schreiben
    
        if (!file) return false;
        string buffer;
        while(getline(cin,buffer))
        {
            if(buffer == "exit") return true;
            file << buffer << "\n";
        }
        return file;
    }
    

    nicht so perfekt 😉

    bool write::fill_in(std::istream& in = std::cin, bool empty = true)
    {
        std::ofstream file_stream(m_file_name.c_str(), empty == true ? std::ios::out : std::ios::app);
        if (!file_stream) return false;
    
        std::string buffer;
        while (std::getline(in, buffer) && buffer != "exit")
            file_stream << buffer << "\n";
    
        return file_stream;
    }
    

    ... bei std::ofstream ist standardmäßig std::ios::out drin. kannst es hier in dem Fall für den einfachen Vergleich aber misbrauchen 😉 Und die Schleife kannst du auch verkürzen (s. Code) und statt return true hättest du sonst auch lieber gucken sollen ob file_stream einen Fehler gefunden hat 😉

    //Methode um Datein auszulesen
    bool read::readout(std::ostream& out) const
    {
        if (!out) return false; // sonst wird hier noch das std::ifstream Objekt erzeugt! ...
    
        std::ifstream file_stream(m_file_name.c_str());
        if (!file_stream) return false;
    
        std::string buffer;
        while (std::getline(file_stream, buffer))
            out << buffer << "\n";
    
        return true;
    }
    


  • Heißt das hier:

    ...empty == true ? std::ios::out : std::ios::app);
    

    soviel wie "wenn empty true ist, dann setzte als Parameter "std::ios::out : std::ios::app", wenn nicht dann setz einfach nichts (also halt default Parameter)?
    Das sowas geht, wusste ich anfangs nicht, weil ich hab das erst so gemacht:

    if(remove) ofstream file(.....);
    else ofstream file(.....,.....);
    

    Aber is ja Unsinn, weil des wird ja nur innerhlab des if-Scope deklariert, und verliert außerhalb dann seine Wertigkeit.
    Und des halb hab ich das mit dem ".open"....gemacht..aber is ja alles schlecht, des von dir is natürlich um einiges besser.
    MfG
    Stromberg



  • nein, es bedeutet "wenn empty true ist, dann setze als parameter ios::out, andernfalls ios::app" (':' bedeutet andernfalls)

    abgesehen davon sollte man mit boolschen werten keine vergleiche machen (weil es ohne auch geht)


Anmelden zum Antworten