Von einer Bildschirmausgabe zum abspeichern in einer Datei



  • Hmm, ich dacht ich quäle mich mit C++... 🙂
    Wenn ich fprintf() benutze bekomme cih folgende Fehler:

    cannot convert `const char*' to `FILE*' for argument `1' to `int fprintf(FILE*, const char*, ...)'

    und das dann für beide printf().

    Muss ich nicht auch erst ne Datei aufmachen wo cih reinschreiben kann?
    Und wie kann ich die Endung von einer Datei ändern? Ich habe im Moment .pls und bräuchte dann den gleichen Dateinamen nur mit .txt, notfalls auch angehängt, müssen sich halt nur unterscheiden.



  • nixversteher schrieb:

    Hmm, ich dacht ich quäle mich mit C++... 🙂

    Ich sehe zumindest nichts in dem Code, was nach C++ aussieht. (btw, in C++ würde ich lieber IO-Streams anstelle von FILE*en verwenden)

    Wenn ich fprintf() benutze bekomme cih folgende Fehler:

    cannot convert `const char*' to `FILE*' for argument `1' to `int fprintf(FILE*, const char*, ...)'

    und das dann für beide printf().

    Muss ich nicht auch erst ne Datei aufmachen wo cih reinschreiben kann?

    Ja, mußt du - woher soll das Programm sonst wissen, wo die Daten hinsollen.

    FILE* fout = fopen(filename2,"w");
    
    ...
    fprintf(fout/*erster Parameter ist das Ziel-FILE*/,.../*ab hier kommen die Parameter aus deinen printf()-Aufrufen*/);
    ...
    

    Und wie kann ich die Endung von einer Datei ändern? Ich habe im Moment .pls und bräuchte dann den gleichen Dateinamen nur mit .txt, notfalls auch angehängt, müssen sich halt nur unterscheiden.

    Den Dateinamen inklusive Endung gibst du beim fopen()-Aufruf an - wenn du da etwas ändern willst, mußt du den Namen nur anpassen (z.B. die letzten drei Zeichen durch "txt" ersetzen).



  • Moin moin,

    unter C++ verwendest du für Konsolen- und Dateiausgaben Streams (cout bzw. ofstream) und damit den Operator<<.

    Du kannst auch Klassen von C-Structs ableiten. So könntest du den Struct DEA_ARCHIV_TYP durchaus ableiten und mit Methoden versehen, welche du dann wie folgt verwenden könntest:

    (Anriss)

    class DeaArchiv : public DEA_ARCHIV_TYP
    {
    // ...
    };
    
    // ...
    
    // Beispielhaft für auslesen aus einer Datei und Ausgabe auf dem Bildschirm
    ifstream infile("daten.txt");
    DeaArchiv arch;
    infile >> arch;
    cout << arch;
    
    // Ausgabe exemplarisch in eine Datei durchführen
    ofstream outfile("neue_daten.txt");
    DeaArchiv arch;
    infile >> arch;
    outfile << arch;
    

    Grüße...

    Heiko



  • Danke, für die flotte Hilfe, nur leider bin ich ein absoluter Anfänger was C++ angeht. Und mach es nur im Zuge meines Praxissemesters.
    Dementsprechend habe ich von Klassen usw. noch überhaupt keinen Plan.

    In der Variablen 'filename' steht der Pfad der zu konvertierenden Dateien drinn, ist es möglich mit fopen() an diesen Dateinamen ein'.txt' anzuhängen?
    Dann hätte ich eine bessere Zuordnung im Verzeichnis. Einmal die nicht konvertierten *.pls Dateien und einmal die Konvertierten *.txt Dateien.

    Habe es eben schon mit

    fopen(filename +".txt", "w")
    

    versucht, nur leider hat das mal garnicht funktioniert. Wie geht das am besten?



  • Ja, filename ist auch "nur" ein char-Zeiger - der unterstützt keine String-Verkettung mit +, sondern bestenfalls mit strcat() (das erfordert allerdings, daß der Speicherplatz ausreicht).



  • Habe jetzt:

    FILE *f = fopen(filename,  "rb");
                        	if (f) {
                         		DEA_ARCHIV_TYP DEARecord;
                           		while(!feof(f)) {
                             			fread(&DEARecord, sizeof(DEARecord), 1, f);
                                			struct tm stm = *localtime(&DEARecord.Startzeit);
                               			    FILE *fout = fopen(filename, "w");
                                   			fprintf(fout,"%02d.%02d.%04d %02d:%02d.%02d,%03d : ", stm.tm_mday
                                      				, stm.tm_mon
                                      				, stm.tm_year + 1900
                                      				, stm.tm_hour
    				                                 , stm.tm_min
    		                                       , stm.tm_sec
    
    				                                , DEARecord.Millisec > 0 ? DEARecord.Millisec : 0);
    
                                             fprintf(fout,"DEARecord: Zustand = %d, Kennung = %d, Grund = %c\n", DEARecord.Wert.Zustand
                                             , DEARecord.Kennung
                                             , DEARecord.Grund);
    
    		                               }
    
                                     fclose(f);
                                     fclose(fout);
    

    Kompilieren geht, allerdings steigt mein Programm jetzt vor der ersten Konvertierung mit der "Main.exe hat ein Problem und muss beendet werden.... An Mircosoft senden?" Nachricht aus!
    Ist es so kompliziert ne Bildschirmausgabe in eine Datei umzulenken? 😕
    Ich verzweifel langsam...



  • Du versuchst da, dieselbe Datei zum Schreiben zu öffnen, die du vorher zum Lesen geöffnet hast. Und du prüfst übrigens nicht, ob das geklappt hat 😉



  • Ok, dann evtl anders, mir wurde ja schon der IO-Stream für C++ empfolen, wie würde ich da die Bildschirmausgabe in eine Datei bekommen?
    Ich habe mit

    ofstream f1 ("test.txt")
    

    einen Stream geöffnet und die vorher nicht vorhandene Datei erzeugt.
    Wie kann ich nun den Inhalt der printf-Anweisung in die Datei lenken?



  • IO-Streams haben ihre eigenen Möglichkeiten, die Ausgaben zu formatieren - die überhaupt keine Ähnlichkeit mit den printf()-Formatierungen zu tun haben. Das heißt, du müsstest deine komplette Ausgabefunktion umbauen.

    (übrigens ist es auch bei fstream notwendig, Fehler abzufangen)



  • nixversteher schrieb:

    Ok, dann evtl anders

    Warum verwendest du nicht einfach nur einen anderen Dateinamen für die Ausgabedatei?



  • Ok, Ausgabe komplett abändern ist gar nciht gut.
    Um das Fehlerabfangen kümmer ich mich noch, ich würde das Programm nur gerne erst mal zum laufen bringen um was in der Hand zu haben damit ich meinen Bericht schreiben kann 😉



  • Dann doch FILE* (so sehr es mich sträubt, das zu empfehlen) - jetzt mußt du "nur" noch den übergebenen Dateinamen umbauen (oder du übergibst der Funktion gleich zwei verschiedene Dateinamen).



  • MFK schrieb:

    Warum verwendest du nicht einfach nur einen anderen Dateinamen für die Ausgabedatei?

    Ich nehme jetzt erst mal einen festen Dateinamen um erst einmal die Funktion zu bekommen.
    Der fprintf funzt bei mir nur überhauptnicht:

    void Konvert(const char *filename)
    {
    
                       	FILE *f = fopen(filename,  "rb");
                        	if (f) {
                         		DEA_ARCHIV_TYP DEARecord;
                           		while(!feof(f)) {
                             			fread(&DEARecord, sizeof(DEARecord), 1, f);
                                			struct tm stm = *localtime(&DEARecord.Startzeit);
                               			    FILE *fout = fopen("test.txt", "w");
                                   			fprintf(fout,"%02d.%02d.%04d %02d:%02d.%02d,%03d : ", stm.tm_mday
                                      				, stm.tm_mon
                                      				, stm.tm_year + 1900
                                      				, stm.tm_hour
    				                                 , stm.tm_min
    		                                       , stm.tm_sec
    
    				                                , DEARecord.Millisec > 0 ? DEARecord.Millisec : 0);
    
                                             fprintf(fout,"DEARecord: Zustand = %d, Kennung = %d, Grund = %c\n", DEARecord.Wert.Zustand
                                             , DEARecord.Kennung
                                             , DEARecord.Grund);
    
    		                               }
    
                                     fclose(f);
    
                                     }
    

    Bei dem Code habe ich beim Kompilieren kein Problem, allerdings schmiert mir das Programm nach 3 Dateien ab und die Konvertierung wird weder ausgegeben noch in die Datei geschrieben.



  • CStoll schrieb:

    Dann doch FILE* (so sehr es mich sträubt, das zu empfehlen) - jetzt mußt du "nur" noch den übergebenen Dateinamen umbauen (oder du übergibst der Funktion gleich zwei verschiedene Dateinamen).

    Das "nur" macht mir etwas Angst.
    Und von mir kommt ein "wie" oder "womit" 😞



  • @Absturz: Hast du mal im Debugger verfolgt, was da passiert? (btw solltest du die Ausgabe-Datei auch wieder schließen, wenn du fertig bist)

    @Umbau des Dateinamens: mit std::string wäre es einfach, mit char* wird es ein wenig komplizierter. Ich würde strrchr() den Punkt im Dateinamen suchen und alle Zeichen dahinter mit der neuen Endung überschreiben (strcpy()).



  • womit arbeitest du nochmal also welcher entwicklungsumgebung?

    versuch es doch mal zu debuggen und schau dir an was bei fread rauskommt und ob nach fopen dein fout überhaupt einen sinnvollen wert hat (also keinen fehlerwert z.B.)

    um die fehlerbehandlung solltest du dich imo sofort kümmern, das hilft meist unerklärliche fehler durchsichtiger zu machen

    *ich sollte noch n kaffee trinken ich bin einfach zu langsam* *GG*



  • Ich arbeite mit Dev-C++.
    Ich versuch es mal mit dem Debugger, danke.



  • Aussager Debugger:

    Eine Zugriffsverletzung (Segmentation Fault) trat in ihrem Programm auf??
    Ist das etwas wovor ich gewarnt wurde? 😕



  • Hi,

    ich glaube nicht, dass das (zuverlässig) geht.
    printf() und IOStreams sind einfach sehr unterschiedliche Konzepte für dieselbe Aufgabe.

    Ich kann Dir nur zeigen, wie man das in C++ eigentlich machen würde:

    #incude <iomanip>
    using std::setw;
    using std::setfill;
    
    ostream& operator<<(ostream& ost, struct tm const * stm) {
       return ost << setw(2) << setfill('0') << stm->tm_mday   << " " 
                   << setw(2) << setfill('0') << stm->tm_mon    << " " 
                   << setw(4) << setfill('0') << stm->tm_year + 1900 << " " 
                   << setw(2) << setfill('0') << stm->tm_hour << " " 
                   << setw(2) << setfill('0') <<  stm->tm_min << " " 
                   << setw(2) << setfill('0') <<  stm->tm_sec;
    }
    
    ostream& operator<<(ostream& ost, DEA_ARCHIV_TYP const& rec) {
       struct tm * stm = localtime(&rec.Startzeit);
       return ost << stm << " "
           << setw(3) << setfill('0') << (rec.Millisec > 0 ? rec.Millisec : 0) << " "
           << "DEARecord: Zustand = " << rec.Wert.Zustand
           << ", Kennung = " << rec.Kennung
           << ", Grund = " << rec.Grund;
    }
    
    FILE *f = fopen(filename,  "rb");
    if (f) {
       DEA_ARCHIV_TYP DEARecord;
       while(!feof(f)) {
          fread(&DEARecord, sizeof(DEARecord), 1, f);
    
          cout << DEARecord << "\n";
    
          ofstream out("Logfile");
          out << DEARecord << "\n";
    
        }
    fclose(f);
    

    Allerdings habe ich jetzt nur die Ausgabe "angepasst".
    Als Designtipp hätte ich noch:

    • Entweder das Einlesen ebenso über den operator>>() machen (geht aber nur, solange DEA_ARCHIV_TYP "default-konstruierbar" ist; sonst über eine Factoryklasse)
    • Lege die Informationen nicht binär ab !

    Zweiteres macht unglaublich Ärger:
    - Das Speicherlayout von Klassen ist nicht festgelegt, ein anderer Compiler, andere Version, andere Compileflags, ... machen damit sofort alle Deine Files ungültig.
    - Bei Debuggen ist das die Hölle ! Nimm lieber "lesbare" Files ! Da finde man alles wieder.
    - Der Unterschied zwischen Deine "Anzeige-Funktion" und der Form, in der die Records in der Datei liegen, ist überflüssig.
    - Mit einem "lesbaren Format", brauchst Du nur einen operator<<().
    - spätestens mit "inneren Verweisen" (z.B. wenn Deine Klasse string, char*, ... enthält), klappt das mit dem "Binärspeichern" sowieso nicht mehr.
    - Zwar wird der "Einlesecode" aufwendiger, aber gleichzeitig wird der Aufruf viel flexibler, eionfacher und "schöner":

    ifstream f(filename);
    ofstream out("Logfile");
    while(f)
       DEA_ARCHIV_TYP DEARecord;
       f >> DEARecord;
       out << DEARecord << "\n";
    }
    

    bzw. (da Du ja offensichtlich mehrere davon einlesen möchtest)

    ifstream f(filename);
    vector<DEA_ARCHIV_TYP> DEARecord;
    copy(istream_iterator<DEA_ARCHIV_TYP>(f), // wendet operator>>(f, ...) an
         istream_iterator<DEA_ARCHIV_TYP>(), // Kennzeichen für "EOF"
         back_inserter(DEARecord));
    

    ... dann hast Du alle DEA_ARCHIV_TYP-Objekte aus der Datei in den Vektor DEARecord gepackt und kannst mittels DEARecord[0], DEARecord[1], .... auf sie zugreifen.

    P.S.: Das Handling von localtime() scheint mir fehlerhaft zu sein (auch, wenn es gelegentlich funktionieren mag). Gemeint ist localtime so, dass man nur den Pointer zurückgibt ... kannst in meinem Code sehen.

    Gruß,

    Simon2.



  • benutz bitte den editier button oben rechts wenn du einem pot etwas hinzufügen möchstest :p

    setz mal bei fread einen breakpoint und bei fopen für die zieldatei, das klingt haargenau danach als ob dasa öffnen fehlschlägt und dein fout ins nirvana zeigt, kann auch ausm fread kommen ... setz einfach die breakpoints und wenn der debugger da steht, lass dir die werte von den varialben f, fout, DEARecord und stm anzeigen ... was mir spanisch vorkommt ist

    struct tm stm = *localtime(&DEARecord.Startzeit);

    kann das wer am rande erklären oder liegt hier evtl. ein fehler ?!

    füge mal

    if (fout == NULL) {
        int Error = GetLastError();
        printf("Fehler %d", Error); <--- breakpoint
    }
    

    nach fopen ein und setz nen breakpoint da ein wenn du dort reinstolperst kannst ja mal nach der beschreibung zu dem fehlercode schauen


Anmelden zum Antworten