Logfile



  • Hallo miteinander!

    Ich bin gerade dabei, einen Konstruktor logfile(...) zu implementieren, so dass das file vom letzten zum ersten in die Datenstruktur geladen wird. (d.h., dass die Reihenfolge "time-reversed" ist, d.h. die letzte Linie im file sollte die erste sein in der internen Datenstruktur).

    Ich hatte zuerst eine Lösung, die allerdings nicht wirklich das machte, was sie hätte machen sollen. 😛
    Also was sicher rein gehört ist das:

    logfile::logfile(std::string logfile_name_)
    {
             //...
    	std::ifstream(logfile_name_);
    }
    

    Das header-file sieht wie folgt aus:

    #ifndef __LOGFILE_HPP__
    #define __LOGFILE_HPP__
    
    #include "record.hpp"
    // STL
    #include <string>
    #include <list>
    #include <fstream>
    
    class logfile
    {
    
        public:
            /**
            * @brief open the file, load the content and keep it open
            */
            logfile(std::string logfile_name_);
    
            void
            print(void);
    
            /**
            * @brief add a new log into the file
            */
            void
            log(std::string message_);
    
        private:
            /* file */
            std::string _logfile_name;
    
            /* structure keeping records */
            std::list<record> _records;
    
    };
    
    #endif  // __LOGFILE_HPP__
    

    Zudem hab ich noch ne (fertig gestellte) record-Klasse. Hier die Header-Datei:

    #ifndef __RECORD_HPP__
    #define __RECORD_HPP__
    
    // STL
    #include <string>
    #include <ctime>
    
    class record
    {
    
        public:
            /**
            * @brief create a new record
            */
            record(int day_, int month_, int year_,
                   int hour_, int minute_, int second_,
                   std::string message_);
    
            int
            get_day(void);
    
            int
            get_month(void);
    
            int
            get_year(void);
    
            int
            get_hour(void);
    
            int
            get_minute(void);
    
            int
            get_second(void);
    
            std::string
            get_message(void);
    
            void
            set_day(int day_);
    
            void
            set_month(int month_);
    
            void
            set_year(int year_);
    
            void
            set_hour(int hour_);
    
            void
            set_minute(int minute_);
    
            void
            set_second(int second_);
    
            void
            set_message(std::string message_);
    
        private:
            /* Date and time information */
            int _day;
            int _month;
            int _year;
            int _hour;
            int _minute;
            int _second;
    
            /* log message reported */
            std::string _message;
    
    };
    
    #endif  // __RECORD_HPP__
    

    Wie könnte eine mögliche Implementation des logfile-Konstruktors aussehen?

    Liebe Grüsse,
    Sonja



  • Zeilenweise die Datei einlesen, in einen record verpacken und jeweils per push_front() in deine Liste verstauen (damit landet der letzte Eintrag der Datei automatisch am Listenanfang).



  • Danke für Deine Antwort!
    Von der Idee her ist mir klar, was du meinst. Aber wie lässt man konkret die Datei zeilenweise einlesen? Kann / muss man da standard-templates benutzen, oder an was hast du gedacht?

    Liebe Grüsse,
    Sonja



  • Dazu müsste ich wissen, wie deine Log-Dateien aufgebaut sind. Aber ich würde die einzelnen Zeilen mit getline() einlesen und dann entsprechend der Spezifikation zerlegen.



  • C++ verbietet eigentlich Namen, die mit einem Unterstrich beginnen. Und Namen, die mit einem Unterstrich enden, sind nicht unbedingt hübsch. Ausserdem würde ich mich dem void in der Parameterliste entledigen.



  • Da die getline-Funktion völlig neu für mich ist, bitte ich um Korrektur meines Codes:

    logfile::logfile(std::string logfile_name_)
    {
    
    	std::vector<std::string> SS;
    	char Zeile[256];
    	std::ifstream infile (logfile_name_, std::ios::in); 
    
    	if (!infile)
    	{
    		std::cout << "Kann Datei nicht öffnen." << std::endl;
    		system("pause");
    	}
    
    	while (infile.getline(Zeile, sizeof(Zeile)))
    	{
    		SS.push_back(Zeile);
    	}
    }
    

    Die Log-Dateien sind wie folgt aufgebaut:

    14 3 111 12 54 48 existing_1_message
    14 3 111 12 55 42 existing_2_message
    

    Ich bin wirklich etwas unsicher, ob mein Code stimmt. Denn das file sollte (so die Aufgabenstellung, neben der bereits erwähnten) geöffnet bleiben, bis das logfile Objekt zerstört wird.

    Die Namen sind halt vorgegeben, und machen (bei dieser Übung) offenbar keine Probleme...

    Ich wäre sehr dankbar, wenn sich jemand den Code anschauen und evtl auch korrigieren könnte.



  • Ich würde eher die globale getline()-Funktion verwenden:

    std::string Zeile;
    ...
    std::getline(infile,Zeile);
    

    Der nächste Schritt wäre es dann, die Zeilen zu zerlegen:

    int tag,monat,jahr,stunde,minute,sekunde;
    std::string nachricht;
    std::istringstream Parser(Zeile);
    Parser >> tag >> monat >> jahr;
    Parser >> stunde >> minute >> sekunde;
    std::getline(Parser,nachricht);
    _records.push_front(record(tag,monat,jahr,stunde,minute,sekunde,nachricht));
    

    SonjaZ schrieb:

    Ich bin wirklich etwas unsicher, ob mein Code stimmt. Denn das file sollte (so die Aufgabenstellung, neben der bereits erwähnten) geöffnet bleiben, bis das logfile Objekt zerstört wird.

    Auch wenn ich nicht weiß wozu es gut ist - dazu muß dein Stream solange geöffnet halten, wie du es brauchst. Das heißt du solltest den Stream als Member der Log-Klasse unterbringen (dann wird er erst im Destruktor wieder freigegeben).



  • Dankeschön!

    Gut, also meinst du, dass ich im Headerfile das speichern sollte:

    protected:	
    std::fstream _file;
    

    ? ..und wie im Source-Code verwenden?

    ..oder hast du es nicht so gemeint?



  • Ja, so hatte ich das gemeint - und verwenden solltest du es anstelle des lokal definierten ifstream, den du dort oben verwendet hast (für solche Situationen hast du dann die Methode open() ).



  • Ahh..raffiniert 😃
    ..noch 2 Fragen hätte ich:

    1.) Kann es an meinem Programm liegen (Visual), oder hab ich was vergessen zu implementieren? Bei deinen Zeilen will es meinen Parser nicht annehmen, zudem jammert der Compiler wegen den "<<" und dem "getline".

    2.) Bei einem Destruktor - reicht dort einfach

    _file.close();
    

    oder muss man dort noch mehr berücksichtigen?



    1. hast du die Stream-Header (fstream und stringstream) eingebunden?

    2. der Destruktor von fstream schließt die verarbeitete Datei automatisch - und der Destruktor deiner Klasse wird auch die Destruktoren der Member aufrufen. Das bedeutet, du mußt dich dort um gar nichts kümmern - der Compiler wird das für die erledigen.
      (das sind die Vorteile von RAII)



    1. #include <iostream> ?
    2. Das müsste reichen.


  • Ok. Also nur um sicher zu gehen: Ich muss gar nichts implementieren, oder lediglich "delete logfile"?

    Ja, ich habe alles eingebunden. Bei " Parser >> tag >> monat >> jahr; " unterliniert Visual einfach das erste ">>" und auch getline wird unterliniert.



  • SonjaZ schrieb:

    Ok. Also nur um sicher zu gehen: Ich muss gar nichts implementieren, oder lediglich "delete logfile"?

    das kommt darauf an, ob du dein Logfile auf dem Stack oder auf dem Heap angelegt hast - wenn du es per new erzeugst, mußt du es auch per delete freigeben. Lokale Variablen werden automatisch entsorgt, wenn sie nicht mehr benötigt werden.

    Ja, ich habe alles eingebunden. Bei " Parser >> tag >> monat >> jahr; " unterliniert Visual einfach das erste ">>" und auch getline wird unterliniert.

    Sorry, das sollte natürlich ein istringstream sein. Und das getline liegt immer noch im Namensraum std.



  • Komisch, <istringstream> wird bei mir nicht gefunden :S
    ..also doch ein Visual-"Problem"?



  • Nein, der Header heißt schon <stringstream>, die Klasse zum Parsen der Logzeile wäre istringstream (i wie input - d.h. sie liest den übergebenen String und veteilt ihn in die Variablen).
    Ich hab' den Code da oben mal überarbeitet, der sollte jetzt so passen.

    PS: Es ist vielleicht eine gute Gelegenheit, dir einen tieferen Blick in die Stream-Bibliothek zu empfehlen 😉



  • Yeah super! 😃
    Eine kleine Änderung, und alles funktioniert 🙂

    Vielen Dank für Deine Hilfe und die Tipps!



  • SonjaZ schrieb:

    Yeah super! 😃
    Eine kleine Änderung, und alles funktioniert 🙂

    Hast du auch verstanden warum? 😉 (Wenn nicht, dann versuche es wenigstens, dann hättest du was gelernt, was wiederum ein Erfolgserlebnis für diejenigen wäre, die dir geholfen haben)



  • Doch, natürlich 🙂
    O --> Output
    I --> Input
    Das wusste ich wirklich nicht, wofür das o und das i jeweils vor den Bibliotheken steht - aber nun ist es ganz klar!

    Ich hätte doch noch eine Programmier-spezifische Frage: Warum kann ich meine print-Funktion nicht so machen?

    for(int i = _records.size(); i <= 0; --i)
    	{
    		std::cout << _records.front() << std::endl;
    		_records.pop_front();
    	}
    

    Der Compiler unterstreicht die "<<" bei cout.



  • Um deine Log-Einträge auszugeben, mußt du der Klasse noch einen Ausgabe-Operator spendieren, der etwa so aussehen könnte:

    ostream& operator<<(ostream& out, const record& daten)
    {
      // den Teil bekommst du alleine hin ;)
      return out;
    }
    

    PS: Ich hab' diese Sofort-Analyse zwar bisher nur mit C# erlebt, aber der MSVS liefert auch eine Liste mit den Fehlertexten, die es angestrichen hat.


Log in to reply