Programm zum Auslesen/Zusammenfassen von Textfiles



  • Hallo ^^
    Ich bin ein kompletter Anfänger im Bereich Programmieren und C++. Trotzdem wurde mir eine, für mich, sehr schwere bis zu schwere Aufgabe gestellt. Das "beste" das ich bisher geschrieben habe ist ein kleines Zahlenratespiel. Meine Aufgabe ist ein Programm zu schreiben das automatisch erstellte Log-Files in eines zusammenfasst und ab einer gewissen Größe ein neues neues anfängt. Ich habe keine Ahnung wie ich an diese Aufgabe rangehen soll. Ein Anfang wäre vielleicht wenn mir jemand sagt mit welchem Befehl ich es überhaupt Mal schaffe ein Textfile auszulesen.
    Hoffe mir kann jemand helfen 😕


  • Mod

    std::ifstream/std::ofstream. Genau so zu benutzen wie alle anderen Streams (die du hoffentlich schon kennst), außer dass man beim Erstellen des Streams einen Dateinamen als Parameter angibt. Genaue Referenzen findest du selber.

    Zur Aufgabe: So schwierig ist das nicht. Lies deine Quelldaten, schreib sie direkt in das Ziel. Zähl dabei mit, wie viel du schon geschrieben hast. Hast du die Zielmenge erreicht, kommt das nächste Ziel.



  • Hey danke für die Antwort 🙂
    Ich hoffe du hast kein Problem damit wenn ich deine Antwort ein wenig "hinterfrage". Soweit ich das jetzt gelesen habe wird mit ifstream ein Puffer erstellt in den das zuerst hineingeschoben wird. Das würde meines Wissens nach aber, je mehr Text es wird, die performance beeinträchtigen. Da der Text vieler Log-Files in eines zusammengefasst werden soll wäre das doch ein unnötiger Arbeitsschritt oder? Gibt es auch einen Befehl mit dem ich einfach den Inhalt eines Textfiles sofort in ein neues schieben kann? Ohne einen Puffer zu erstllen? Dazu sollte ich noch sagen das die alten Log-Files aber nicht verändert werden dürfen. Oder habe ich da etwas falsch aufgefasst?



  • Kopieren von Daten von einem File in ein anderes geht so-gut-wie immer: Lesen aus einem File in den Speicher, Schreiben vom Speicher in das andere File. Fertige Funktionen wie z.B. CopyFile bzw. auch Commandlinebefehle ala copy/cp/xcopy/... machen im Hintergrund auch nichts anderes.
    Und Speicher ist schnell. Irre schnell. Heute typischerweise zig Gigabyte pro Sekunde.

    Was du nicht unbedingt tun solltest ist Zeichen für Zeichen zu kopieren. Mach nen Puffer mit ein paar KB - vielleicht maximal 1 MB oder so - und verwende den.



  • Vielen Dank für eure Hilfe!
    Ich habe es jetzt schon ein Mal so weit geschafft, dass ich eine gewisse Textdatei öffnen kann und dessen Inhalt in Eine neue kopiere. Jedoch habe ich jetzt gleich mehrere Fragen und hoffe ihr könnt mir diese beantworten.

    ofstream Zieldatei("Neues Log-File.txt");
    

    So sieht momentan meine Bezeichnung des neuen Log-Files aus in das alles hineinkopiert wird. Das Programm das ich schreibe soll diese Files jedoch immer automatisch mit dem Datum dieses Tages bezeichnen. Wie z.B.: "09.02.2016 Log-File 1". Und wenn das über eine gewisse Größe geht (da weiß ich schon wie ich das mache) soll es das nächste "09.02.2016 Log-File 2" nennen. Wie kriege ich das hin?

    Quelldatei.open("../../Log Files/TraxisService.log.[number]", ios_base::in);
    

    So habe ich das gelöst mit dem öffnen der Log-Files dessen Inhalt dann kopiert wird. Jedoch gibt es nicht nur ein Log-File wodurch ich versuche das mit einer Schleife zu lösen. In jedem Durchlauf wird "number" um eins erhöht. Wird das dann funktionieren, so das es beim ersten Durchlauf "TraxisService.log.1" öffnet, beim zweiten ""TraxisService.log.2", usw.?

    3. und Letztens
    Wenn ich dieses Programm so durchlaufen lassen würde, würde er jedoch jedes Mal ein neues File generieren. Wie lautet der Befehl mit dem ich etwas in bereits vorhandene Files kopieren kann? 😕 😕

    Mein Kopf raucht und ich hoffe es hat jemand Zeit mir zu Helfen. Danke schon Mal im voraus ^^


  • Mod

    Wenn die Dateinamen veränderlich sein müssen, dann musst du eben eine veränderliche Zeichenkette als Dateiname benutzen. Von std::string hast du sicher schon gehört? Mit std::to_string kann man Zahlen in std::string umwandeln und sich dann etwas basteln. Oder wenn man komplexere Bastelaufgaben hat, gibt es auch noch std::stringstream, das ist wieder ein Stream, mit dem gleichen Interface das du schon kennst, bloß dass im Hintergrund ein std::string als Quelle/Ziel steht, den du dir nach der Bastelei über die str() Funktion holen kannst.

    Datum? Wenn du diese Aufgabe bekommen hast, dann musst doch entweder schon einmal vorgekommen sein, wie man an das Datum kommt oder euch müssen Techniken beigebracht worden sein, wie man so etwas herausfinden kann. Wie geht man so etwas an? Google: c++ date string sollte das erste sein, das dir in den Kopf kommt. Oder eine Referenz benutzen:
    http://en.cppreference.com/w/
    Utilities library->Date and time. Klingt vielversprechend. Duration/Dauer? Nicht was wir suchen. clock/Uhr? Auch nicht, da clock hier eher im Sinne von Stoppuhr gemeint ist. Time point/Zeitpunkt? Bingo! Hier jedoch laufen wir ein bisschen ins Leere, denn wie man mit Zeitpunkten umgeht hat uns eigentlich nicht so sehr interessiert, wir wollten eher wissen, wie wir das aktuelle Datum erhalten und wie wir es ausgeben können. Glücklicherweise demonstriert das Beispiel auf der Seite genau das, was wir brauchen:

    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
        std::time_t now_c = std::chrono::system_clock::to_time_t(now - std::chrono::hours(24));
        std::cout << "24 hours ago, the time was "
                  << std::put_time(std::localtime(&now_c), "%F %T") << '\n';
    

    Mittels der now() Funktion der std::chrono::system_clock bekommen wir also die Uhrzeit und mittels std::put_time kann man Zeiten in Zeichenketten umwandeln. Wenn wir mehr Informationen dazu brauchen, suchen wir deren Beschreibung in der Referenz auf.

    PS: std::time_put ist eine etwas neuere Zugabe zum C++-Standard. Falls du einen älteren Compiler benutzt, musst du eventuell auf die alten Funktionen aus dem C-Standard ausweichen:
    http://en.cppreference.com/w/cpp/chrono/c
    In dem Fall müsstest du mit char-Arrays herum basteln. Nicht zu empfehlen, wenn du nicht genau weißt, was du tust.



  • Hallo Tehiyok,

    Ist schon lustig. Du schreibst selber dass Du "kompletter Anfänger im Bereich Programmieren und C++" bist, und Dein erster Gedanke dreht sich um Performance.
    Mein Tipp: schreibe zunächst ein funktionsfähiges Programm, und wenn das dann in Sachen Geschwindigkeit Deinen Ansprüchen nicht genügt, dann können wir hier gemeinsam über Beschleunigungsmassnahmen nachdenken.

    Das Schreiben des Inhalts einer Datei in eine andere ist auch mit C++ relativ einfach. Im Prinzip kannst Du folgendes machen:

    ofstream Zieldatei("Neues Log-File.txt");
        ifstream Quelldatei("../../Log Files/TraxisService.log.[number]"); // das ios_base::in kann man sich schenken (i)fstream ohne 'in' geht nicht
        Zieldatei << Quelldatei.rdbuf();  // Inhalt von 'Quelldatei' nach 'Zeildatei' schreiben
    

    wie das Ganze dann zusammen spielen könnte habe ich hier mal skizziert:

    #include <iostream>
    #include <fstream>
    #include <string>
    
    int main()
    {
        using namespace std;
        auto const maxSenkenGroesse = 10000000;  // Groesse des Output-Files in Byte
    
        int logNr = 1;      // erste Nummer für das File 'TraxisService'
        int quelleNr = 1;   // erste Nummer für die Quelldatei
        string logName = "datum LogFile "; // zum Datum s. Beitrag von SeppJ
        for( bool error = false; !error; ++logNr )
        {
            ofstream senke( logName + to_string(logNr) + ".txt" );
            for( ; senke.tellp() < maxSenkenGroesse; ++quelleNr )
            {
                ifstream quelle( "../../Log Files/TraxisService.log." + to_string(logNr) );
                if( !quelle.is_open() )
                {
                    error = true;
                    break;
                }
                senke << quelle.rdbuf(); // hier wird der Inhalt des Files TraxisService an das File LogFile angehängt
            }
        }
        return 0;
    }
    

    Gruß
    Werner



  • Als kurze Erklärung. Ich bin momentan streng genommen Lehrling in der Telekommunikationselektronik. An sich habe ich, von meinem "Beruf" her mit programmieren nichts am Hut. Da mich das Thema Programmieren schon immer interessiert hat, habe ich ,wenn ich in der Firma nichts zu tun hatte, mich immer hingesetzt und mit einem Buch angefangen mir das selbst beizubringen. Sprich alles was ich kann habe ich mir selbst beigebracht.
    Das ist meiner Abteilung aufgefallen und sie sind auf die Idee gekommen das zu fördern (was ich echt toll fand) da ihnen sowieso ein Programmierer fehlt. Und da hatte jemand den Einfall mir diese Aufgabe zu geben im Sinne von "an schweren Aufgaben wächst man".

    Das habe ich jetzt deswegen erwähnt weil ich damit sagen will, ich habe weder eine Ahnung von "strings" noch von diesen streams à la ofstream/ifstream usw.
    Die Themen die ich durch habe sind "Variablen, Schleifen, den Anfang von Funktionen und jetzt den Anfang von Arrays".

    Denkt ihr das es sinnvoll wäre jetzt einfach zu versuchen die Aufgabe zu erledigen ohne es unbedingt zu verstehen oder macht das eher wenig Sinn?



  • Tehiyok schrieb:

    Denkt ihr das es sinnvoll wäre jetzt einfach zu versuchen die Aufgabe zu erledigen ohne es unbedingt zu verstehen oder macht das eher wenig Sinn?

    Es macht selten Sinn, Aufgaben zu erledigen, die man nicht versteht.



  • Tehiyok schrieb:

    ... ich habe weder eine Ahnung von "strings" noch von diesen streams à la ofstream/ifstream usw.
    Die Themen die ich durch habe sind "Variablen, Schleifen, den Anfang von Funktionen und jetzt den Anfang von Arrays".

    Denkt ihr das es sinnvoll wäre jetzt einfach zu versuchen die Aufgabe zu erledigen ohne es unbedingt zu verstehen oder macht das eher wenig Sinn?

    Es macht auf jeden Fall Sinn, sich mit dem Thema 'string' und 'stream' in C++ mal zu beschäftigen. Zumindest so weit, dass Du mit obigen Code etwas anfangen kannst.
    Das sollte nicht allzu schwierig sein.

    Gruß
    Werner



  • Es macht auf jeden Fall Sinn, sich mit dem Thema 'string' und 'stream' in C++ mal zu beschäftigen. Zumindest so weit, dass Du mit obigen Code etwas anfangen kannst.
    Das sollte nicht allzu schwierig sein.

    Ich meinte nicht ob ich es generell lernen soll/muss. Meine Frage ging eher in die Richtung ob ich versuchen sollte die Aufgabe zu lösen im Schema von "Es funktioniert jetzt alles, aber ich verstehe NOCH nicht wie". Weil es wird halt schon ein wenig Zeit in Anspruch nehmen das alles zu verstehen und viel Zeit habe nicht.
    Naja ich hätte da noch eine Frage. Ich habe ja gefragt wie ich die neu erstellte Datei automatisch nach dem Datum des Tages benennen kann. Das:

    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
        std::time_t now_c = std::chrono::system_clock::to_time_t(now - std::chrono::hours(24));
        std::cout << "24 hours ago, the time was "
                  << std::put_time(std::localtime(&now_c), "%F %T") << '\n';
    

    oder

    string logName = "datum LogFile "; // zum Datum s. Beitrag von SeppJ
    

    das funktioniert aber nicht. Bei dem ersten Code wird mir nur Datum und Zeit angezeigt. Beim zweiten wird die Datei "datum LogFile" genannt. Ich möchte es aber hinbekommen das jedes Mal wenn ein neues LogFile generiert wird, es als Namen das Datum dieses Tages hat. Sprich wie kann ich die Zeit automatisch einer Datei als Dateinamen zuweißen?


  • Mod

    Tehiyok schrieb:

    Ich meinte nicht ob ich es generell lernen soll/muss. Meine Frage ging eher in die Richtung ob ich versuchen sollte die Aufgabe zu lösen im Schema von "Es funktioniert jetzt alles, aber ich verstehe NOCH nicht wie". Weil es wird halt schon ein wenig Zeit in Anspruch nehmen das alles zu verstehen und viel Zeit habe nicht.

    Das ist aber extrem wichtig. Programmieren ist nicht Code aus dem Internet zusammen zu kopieren. Code ist Logik pur. Du musst genau wissen, was du tust. Du musst von jedem einzelnen Zeichen in deinem Code verstehen, wo und warum du es setzt.

    Beweis:

    Naja ich hätte da noch eine Frage. Ich habe ja gefragt wie ich die neu erstellte Datei automatisch nach dem Datum des Tages benennen kann. Das:

    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
        std::time_t now_c = std::chrono::system_clock::to_time_t(now - std::chrono::hours(24));
        std::cout << "24 hours ago, the time was "
                  << std::put_time(std::localtime(&now_c), "%F %T") << '\n';
    

    oder

    string logName = "datum LogFile "; // zum Datum s. Beitrag von SeppJ
    

    das funktioniert aber nicht. Bei dem ersten Code wird mir nur Datum und Zeit angezeigt. Beim zweiten wird die Datei "datum LogFile" genannt. Ich möchte es aber hinbekommen das jedes Mal wenn ein neues LogFile generiert wird, es als Namen das Datum dieses Tages hat. Sprich wie kann ich die Zeit automatisch einer Datei als Dateinamen zuweißen?

    Was denkst du denn, was die Codes machen? Was sollte wohl der Kommentar von Werner bedeuten? Das erste ist ein Beispiel für ein ähnliches Problem und das zweite ist der Hinweis, dass du es so machen sollst wie im ersten Beispiel. Wenn du nicht einmal erkennst, das etwas ein Beispiel ist, wie willst du dann jemals eigenständig etwas programmieren können?



  • Was denkst du denn, was die Codes machen? Was sollte wohl der Kommentar von Werner bedeuten? Das erste ist ein Beispiel für ein ähnliches Problem und das zweite ist der Hinweis, dass du es so machen sollst wie im ersten Beispiel. Wenn du nicht einmal erkennst, das etwas ein Beispiel ist, wie willst du dann jemals eigenständig etwas programmieren können?

    Dazu hätte ich erwähnen sollen das ich ungefähr wusste wie ich mir die Zeit "anzeigen" lasse, ich habe lediglich keine Ahnung wie ich das als Dateiname für eine neue Datei verwende bzw. damit verbinde.
    Ich habe also sehr wohl verstanden das es nur ein Beispiel war, allerdings war das gezeigte mir schon ungefähr bekannt und das was ich wissen wollte weiß ich noch immer nicht. Und dann einen Programmieranfänger der sich nur mit besagten Dingen auskennt(Variablen, Schleifen, den Anfang von Funktionen und jetzt den Anfang von Arrays) zu fragen wie er dann jemals selbständig programmieren will finde ich etwas... naja.
    Und die Antwort auf deine Frage wäre, wenn ich mich besser auskenne. Die mir auferlegte Aufgabe übersteigt einfach mein momentanes Wissen.



  • Hallo,

    .. nur die Ruhe. Deine Frage war:

    Tehiyok schrieb:

    wie kann ich die Zeit automatisch einer Datei als Dateinamen zuweißen?

    Die führende Aussage:

    Tehiyok schrieb:

    das funktioniert aber nicht. Bei dem ersten Code wird mir nur Datum und Zeit angezeigt. Beim zweiten wird die Datei "datum LogFile" genannt.

    war in diesem Kontext etwas irreführend.

    Du kannst das Problem über einen std::ostringstream lösen. Als Anfänger kannst Du das auch nicht wissen. Sieht ungefähr so aus:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <chrono>
    #include <ctime>    // std::time_t
    #include <iomanip>  // put_time
    #include <sstream>  // ostringstream
    
    int main()
    {
        using namespace std;
        auto const maxSenkenGroesse = 10000000;  // Groesse des Output-Files in Byte
    
        int logNr = 1;      // erste Nummer für das File 'TraxisService'
        int quelleNr = 1;   // erste Nummer für die Quelldatei
    
        std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
        std::time_t now_c = std::chrono::system_clock::to_time_t(now - std::chrono::hours(24));
        ostringstream buffer;
        buffer << std::put_time(std::localtime(&now_c), "%Y-%m-%d %H:%M:%S"); // s. http://www.cplusplus.com/reference/ctime/strftime/
        string logName = buffer.str() + " LogFile "; // Datum davor hängen
    
        // usw. wie oben ab Zeile 13
    

    Gruß
    Werner



  • Danke für deine Hilfe Werner.
    Allerdings stehe ich jetzt vor einem noch größeren Problem, bei dem mir bisher auch niemand sagen konnte wie ich es lösen kann.

    if (number = ...)
    

    number erhöht sich in jedem Schleifendurchlauf um 1 und beginnt bei 1. Jetzt soll Folgendes passieren. Es soll jedes Mal wenn number 1, 6, 11, 16, 21, 26 usw. ist die Bedingung wahr sein. Sprich jedes 5. Mal von 1 weg soll sich die Bedingung aktivieren. Wie bekomme ich das hin? Mit einer Gleichung kann ich das nicht schaffen oder? Bei den Zahle gibt es ja ein Schema, kann ich dieses irgendwie als Bedingung in die Klammer setzen? Ich häng da grad seit 2h davor und komme nicht drauf, selbst mit google nicht.



  • Hallo,

    Tehiyok schrieb:

    number erhöht sich in jedem Schleifendurchlauf um 1 und beginnt bei 1. Jetzt soll Folgendes passieren. Es soll jedes Mal wenn number 1, 6, 11, 16, 21, 26 usw. ist die Bedingung wahr sein. Sprich jedes 5. Mal von 1 weg soll sich die Bedingung aktivieren. Wie bekomme ich das hin? Mit einer Gleichung kann ich das nicht schaffen oder?

    doch sicher!
    ich kenne Deine Schulbildung nicht. Hast Du schon mal was vom Modulo gehört?

    Die entsprechende Funktion bzw. der Operator in C++ ist das '%'-Zeichen. Etwa so:

    if( (number % 5) == 1 ) // '==' dient zum Vergleich; '=' wäre eine Zuweisung!
        {   // number hat jetzt einen Wert von {1, 6, 11, 16, 21, ...}
            // usw. Bedingung ist eingetreten
    

    Tehiyok schrieb:

    ... bei dem mir bisher auch niemand sagen konnte wie ich es lösen kann.

    erstaunlich!

    Gruß
    Werner

    PS.:

    Tehiyok schrieb:

    ... und komme nicht drauf, selbst mit google nicht.

    Denken ist wie googeln; nur krasser!



  • Vielen lieben Dank für deine Hilfe Werner!
    Ich habe in meiner Schulausbildung noch nie Modulo verwendet. Allerdings bin ich bei meinem Selbststudium darauf gestoßen. Ich habe aber nicht ganz verstanden für was bzw. welche Verwendungszwecke es hat. Jetzt weiß ich es! 🙂



  • Hallo,

    ich hätte da ein weiteres Problem.

    ifstream Quelldatei;
             Quelldatei.open("../../Log Files/TraxisService.log."[number], ios_base::in);
    

    Folgendes Problem. Diese LogFiles heißen "TraxisService.log.1" "TraxisService.log.2" usw. Pro Durchlauf der Schleife soll der Inhalt eines dieser Files ausgelesen und in ein anderes/neues kopiert werden. Ich dachte das ich das mit obigem Code hinkriege in dem ich eine Variable dahinter setzte und diese pro Durchlauf um eines hochzähle. Das funktioniert natürlich nicht. Habe ich einfach etwas falsch geschrieben oder ist der Denkansatz schon falsch?


  • Mod

    Tehiyok schrieb:

    Habe ich einfach etwas falsch geschrieben oder ist der Denkansatz schon falsch?

    Letzteres. Du hast offensichtlich 0 aus diesem Thread gelernt.



  • Letzteres. Du hast offensichtlich 0 aus diesem Thread gelernt.

    Ich weiß nicht warum deine Antworten immer so offensiv sein müssen? Ich meine gesagt zu haben das ich Anfänger bin und mich nicht gut auskenne. Und doch ich habe schon etwas aus den Antworten hier gelernt. Anscheinend aber nicht das was mir hier weiterhilft.
    Und wieso antwortest du mir überhaupt wenn das alles ist was du zu sagen hast? Das ist weder hilfreich noch produktiv.


Log in to reply