Datei zeileneise auslesen



  • Hej,

    ich bin noch ein ziemlicher C++ Neuling und kämpfe gerade mit dem folgenden Problem rum: Ich möchte ein Programm schreiben, welches eine (Log)Datei zeilenweise ausliest und die Zeilen nach einem bestimmten Muster durchsucht.
    Im ersten Schritt wollte ich einfach mal ein Programm schreiben, dass überhaupt mal eine Textdatei öffnet und sie zeilenweise durchgeht und dann die Zeilen in die Konsole schreibt.
    Nach ein bisschen googlen habe ich mir folgenden Code zusammengeklaut:

    #include <iostream>
    #include <istream>
    #include <fstream>
    #include <string>
    
    using namespace std;
    
    int main() {
    
    	char line[256];
    
    	  ifstream infile ("D:\\Logs\\log.txt", ios::in);
    
    	  if (!infile)
    	    {
    	      cout << "Kein File :-(" << endl;
    	      return 1;
    	    }
    
    	  while (infile.getline (line, 256))
    	    {
    	      // mach was mit der zeile
    	      cout << line << endl;
    	    }
    	  cout << endl;
    
    	  return 0;
    	}
    

    Das Programm tut es auch mehr odre weniger, allerdings geht es nicht die gesamte Logdatei durch, sondern bricht einfach irgendwann ab. Dazu sollte man vllt anmerken, dass die Logs sehr groß werden können und ich vermute, dass es vllt Speichermäßig nicht ausreicht.
    Kann mir da jmd auf die Sprünge helfen, ob ich in dem Code was vergessen habe oder Ansatz mit ifstream nicht der Richtige ist und es besser Alternativen für dieses Problem gibt?
    So nebenbei gefragt (für meine nächste Ausbaustufe schon mal): Wenn ich in Dateien nach Zeilen suche, die einem bestimmten Muster entsprechen, soll ich da regex nehmen oder gibt es was simpleres? Am besten std-Bib? Oder was würdet ihr mir da empfehlen?

    Viele grüße und danke schonmal im vorraus.



  • Zeig mal den Aufbau der Logdatei und welches Muster, also was genau, du dann habe willst...



  • Ich tippe mal darauf, dass eine Zeile > 256 Zeichen hat. Kann das sein?
    Wie auch immer, warum nicht ein std::string?

    ifstream is("D:/Logs/log.txt"); // Wir nehmen immer Slashes.
    
    if(!is)
    {
        ...
    }
    
    for(string line; getline(is, line);)
    {
        cout << line << '\n';
    }
    


  • Kellerautomat schrieb:

    ifstream is("D:/Logs/log.txt"); // Wir nehmen immer Slashes.
    

    Wozu? Pfade sind sowieso plattformabhängig.
    Bei relativen Pfaden würde es noch sinn machen, damit diese unter Windows und Linux funktionieren.
    Bei absoluten Pfaden wie diesem hier... der funktioniert sowieso nur unter Windows.
    Und dem Standard ist es egal.

    (Bei #include machen fwd.-slashes mMn. wirklich Sinn, vor allem wenn der Code plattformunabhängig oder zumindest leicht portierbar ist.)



  • mo9ua schrieb:

    Dazu sollte man vllt anmerken, dass die Logs sehr groß werden können und ich vermute, dass es vllt Speichermäßig nicht ausreicht.

    Das sollte egal sein, da du die Datei ja zeilenweise einliest.

    Kann mir da jmd auf die Sprünge helfen, ob ich in dem Code was vergessen habe oder Ansatz mit ifstream nicht der Richtige ist und es besser Alternativen für dieses Problem gibt?

    Probier es mal mit std::string wie im Beispiel von Kellerautomat.

    So nebenbei gefragt (für meine nächste Ausbaustufe schon mal): Wenn ich in Dateien nach Zeilen suche, die einem bestimmten Muster entsprechen, soll ich da regex nehmen oder gibt es was simpleres? Am besten std-Bib? Oder was würdet ihr mir da empfehlen?

    Das kommt wirklich auf das Muster drauf an, und darauf was du unter "was simpleres" verstehst. Viel einfacher zu programmieren als mit Regexen wird es wohl in den wenigsten Fällen. Einfacher für die CPU (=weniger Rechenzeit) kann dagegen leicht sein -- manche Muster lassen sich mit Regexen nicht wirklich performant suchen.



  • hustbaer schrieb:

    Kellerautomat schrieb:

    ifstream is("D:/Logs/log.txt"); // Wir nehmen immer Slashes.
    

    Wozu? Pfade sind sowieso plattformabhängig.

    backslashes sind fehle\ra\nfällig



  • rückhieb schrieb:

    hustbaer schrieb:

    Kellerautomat schrieb:

    ifstream is("D:/Logs/log.txt"); // Wir nehmen immer Slashes.
    

    Wozu? Pfade sind sowieso plattformabhängig.

    backslashes sind fehle\ra\nfällig

    Was hat das damit zu tun?



  • Sone schrieb:

    rückhieb schrieb:

    hustbaer schrieb:

    Kellerautomat schrieb:

    ifstream is("D:/Logs/log.txt"); // Wir nehmen immer Slashes.
    

    Wozu? Pfade sind sowieso plattformabhängig.

    backslashes sind fehle\ra\nfällig

    Was hat das damit zu tun?

    Die Nötigkeit Backlashes zu escapen macht sie unnötig fehleranfälliger in der Anwendung als Slashes.

    // edit: Deutsches Sprache...



  • Swordfish schrieb:

    Sone schrieb:

    rückhieb schrieb:

    hustbaer schrieb:

    Kellerautomat schrieb:

    ifstream is("D:/Logs/log.txt"); // Wir nehmen immer Slashes.
    

    Wozu? Pfade sind sowieso plattformabhängig.

    backslashes sind fehle\ra\nfällig

    Was hat das damit zu tun?

    Die Nötigkeit Backlashes zu escapen macht sie unnötig fehleranfälliger in der Anwendung als Slashes.

    // edit: Deutsches Sprache...

    Nein nein nein... 😃
    Ich meinte hustbaers Aussage, dass Pfade vom Standard gar nicht berücksichtigt werden.

    Das mit Backslashes sollte doch nun wirklich jeder kennen.



  • Sone schrieb:

    Swordfish schrieb:

    Sone schrieb:

    rückhieb schrieb:

    hustbaer schrieb:

    Kellerautomat schrieb:

    ifstream is("D:/Logs/log.txt"); // Wir nehmen immer Slashes.
    

    Wozu? Pfade sind sowieso plattformabhängig.

    backslashes sind fehle\ra\nfällig

    Was hat das damit zu tun?

    Die Nötigkeit Backlashes zu escapen macht sie unnötig fehleranfälliger in der Anwendung als Slashes.

    Ich meinte hustbaers Aussage, dass Pfade vom Standard gar nicht berücksichtigt werden.

    Bei relativen Pfaden bleibt - wie schon oben angemerkt - der Vorteil, daß den Slash mehr Betriebssysteme verstehen als den Backlash.



  • Ja und lustig wird es dann wenn der Pfad an irgendeine Komponente (DLL, Commandline-Programm, was auch immer) übergeben wird die dummerweise mit / nix anfangen kann weil sie \ erwartet. Unter Windows sind solche Komponenten nicht gerade selten.



  • hustbaer schrieb:

    Ja und lustig wird es dann wenn der Pfad an irgendeine Komponente (DLL, Commandline-Programm, was auch immer) übergeben wird die dummerweise mit / nix anfangen kann weil sie \ erwartet. Unter Windows sind solche Komponenten nicht gerade selten.

    Sollte man nicht immer einfach std::replace drüberlaufen lassen? Wie man auch die Line-Endings immer ersetzt?



  • Was hilft das wenn es die Komponente/das externe Programm nicht tut?
    Weiss du, man hat nicht für alles nen Source, Freund Hacker.

    Und wenns im eigenen Programm ist... ich kann mir grad nix sinnloseres vorstellen als Pfade in String-Konstanten mit / reinschreiben und dann mittels replace auf \ zu ersetzen. Kannst du?



  • hustbaer schrieb:

    [...] lustig wird es dann wenn der Pfad an irgendeine Komponente [...] übergeben wird die dummerweise mit / nix anfangen kann weil sie \ erwartet. [...]

    Jo, donn is' oha ...



  • hustbaer schrieb:

    Was hilft das wenn es die Komponente/das externe Programm nicht tut?
    Weiss du, man hat nicht für alles nen Source, Freund Hacker.

    Und wenns im eigenen Programm ist... ich kann mir grad nix sinnloseres vorstellen als Pfade in String-Konstanten mit / reinschreiben und dann mittels replace auf \ zu ersetzen. Kannst du?

    Nö. Das ist ehrlich gesagt doof, wenn diese "Komponente" von solchen Kleinigkeiten abhängig ist... 😞



  • Sone schrieb:

    hustbaer schrieb:

    Was hilft das wenn es die Komponente/das externe Programm nicht tut?
    Weiss du, man hat nicht für alles nen Source, Freund Hacker.

    Und wenns im eigenen Programm ist... ich kann mir grad nix sinnloseres vorstellen als Pfade in String-Konstanten mit / reinschreiben und dann mittels replace auf \ zu ersetzen. Kannst du?

    Nö. Das ist ehrlich gesagt doof, wenn diese "Komponente" von solchen Kleinigkeiten abhängig ist... 😞

    Und die Realität zu verleugnen ist schlau?



  • Also erstmal danke für die ganzen Antworten 🙂
    Das mit den Backslashes hab ich mir jetzt hinter die Ohren geschrieben, dass ich die nicht in diesem Kontext benutzen soll.
    Die Logdatei kann, wie erwähnt, groß sein => >256 kb.
    Die Zeile, nach der ich suche schaut ca. so aus:

    2012/07/03 02:17:25.267 INFO  Unwichter Text: { XXXX XXXX XXXX XXXX XXXX XXXX }
    

    Ich muss nun auf die Elemente in der geschweiften Klammer zugreifen können bzw. sie zählen in der gesamten Datei.

    Ergo meine Idee war, dass ich die Log zeilenweise durchgehe und immer wenn ich so eine Zeile finde, dann die Elemente in einen Hash einfüge. Wobei die Elemente (XXXX) wären die Schlüssel des Hashs und die Anzahl der Vorkommen des jeweiligen Elements ist dann der Wert des Hashs (Wert -> Anzahl).
    Ich glaube, statt Hash nimmt man bei C++ map oder sowas.

    Nachtrag: Ob die Zeile größer oder kleiner 256 ist dürfte ja eigentliche egal sein. Das schlimmste was dann passieren mmüsste, ist dass eine Zeile nicht richtig dargestellt wird, sonst nicht. Das Problem ist aber, dass nicht alle zeilen eingelesen werden aus irgend einem Grund.

    Nachtrag2: Hab die char-Variable "line" auf 1024 vergrößert, jetzt gehts. Jetzt bleibt nur noch die Frage nach dem suchen der richtigen Zeilen und dem extrahieren der richtigen Zeilen daraus.



  • mo9ua schrieb:

    Nachtrag: [...]
    Nachtrag2: [...]

    Kellerautomat schrieb:

    [...] Wie auch immer, warum nicht ein std::string?



  • mo9ua schrieb:

    Die Logdatei kann, wie erwähnt, groß sein => >256 kb.
    Die Zeile, nach der ich suche schaut ca. so aus:

    2012/07/03 02:17:25.267 INFO  Unwichter Text: { XXXX XXXX XXXX XXXX XXXX XXXX }
    

    Ich muss nun auf die Elemente in der geschweiften Klammer zugreifen können bzw. sie zählen in der gesamten Datei.

    Ergo meine Idee war, dass ich die Log zeilenweise durchgehe und immer wenn ich so eine Zeile finde, dann die Elemente in einen Hash einfüge. Wobei die Elemente (XXXX) wären die Schlüssel des Hashs und die Anzahl der Vorkommen des jeweiligen Elements ist dann der Wert des Hashs (Wert -> Anzahl).
    Ich glaube, statt Hash nimmt man bei C++ map oder sowas.

    Hallo mo9ua,

    ja - hier wäre eine std::map angebracht.
    Du solltest uns noch sagen, inwiefern sich so eine Zeile mit den Schlüsseln von allen anderen Zeilen unterscheidet. Ist z.B. das Vorkommen von '{' bereits exklusiv?
    Dann ist es einfach. Man lese alles bis man auf ein '{' trifft und anschließend lese man die Worte bis ein '}' auftaucht.
    Oder ist das Wort 'INFO' das, was den entscheidenden Unterschied zu den anderen Zeilen ausmacht?

    Anbei eine Code-Skizze für den Fall mit exklusivem '{':

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <map>
    #include <limits> // std::numeric_limits<>
    
    int main()
    {
        using namespace std;
        map< string, int > keys;
        {
            ifstream logdatei("D:/Logs/log.txt");
            if( !logdatei.is_open() )
            {
                cerr << "Fehler beim Oeffnen der Datei" << endl;
                return 0;
            }
            for( ; logdatei.ignore( numeric_limits< streamsize >::max(), '{' ); )
            {
                for( string token; logdatei >> token && token != "}"; )
                    ++keys[token] ;
            }
        }
        cout << "Haeufigkeit der Schluessel:" << endl;
        for( map< string, int >::iterator i = keys.begin(); i != keys.end(); ++i )
            cout << i->first << ": " << i->second << endl;
        return 0;
    }
    

    Dabei ist wichtig, dass zwischen dem letzten Schlüssel einer Zeile und dem '}' noch mindestens ein Leerzeichen(oder Tab) stehen muss, sonst wird das Ende nicht erkannt!

    Gruß
    Werner

    @Edit: Tippfehler beseitigt


Anmelden zum Antworten