Frage zu ein Beispielprogramm aus einen Buch



  • /* shreeder.c */
    #include <stdio.h>
    #include <stdlib.h>
    
    void my_remove(char *rm) {
       unsigned long size;
    
       FILE *rem=fopen(rm, "w+b");
       if(rem != NULL)  {
          fseek(rem, 0L, SEEK_END);  /* Stream an das Dateiende   */
          size=ftell(rem);           /* Größe in Bytes ermitteln */
          /* kompletten Inhalt mit \0 überschreiben */
          fwrite((char *)'\0', 1, size, rem);
          /* damit die Datei gelöscht werden kann, schliessen */
          fclose(rem);
          remove(rm);    /* Jetzt weg damit */
       }
    }
    
    int main(void) {
       char datei[] = "test.txt";
       my_remove(datei);
       return EXIT_SUCCESS;
    }
    

    Hallo, ich bin gerade am versuchen mir C einbisschen näher zu bringen. Dabei bin ich auf das obere Beispiel gestoßen. In meinen Augen sind doch die Anweisungen fseek (Dateianfang), ftell (weist 0 zu) und fwrite(macht gar nichts) im Code suspekt, weil doch durch "w+b" der Inhalt der Datei gar nicht mehr vorhanden ist, oder ?
    Mfg
    Tom



  • Aus welchem Buch stammt das Beispiel?

    halloC schrieb:

    fseek (Dateianfang)

    Nein, ans Dateiende:

    fseek(rem, 0L, SEEK_END);  /* Stream an das Dateiende   */
    


  • Hallo,
    ja das ist doch nach der Anweisung "w+b" doch gleichzeitig der Dateianfang,oder nicht ?
    Ich habe dieses Beispiel aus den Buch "C von A bis Z" .
    Mfg
    Tom



  • Nein w+b bedeutet, dass du die Datei binär lesen und schreiben kannst, fseek sucht eine bestimmte stelle in der Datei, ftell gibt die dateigröße, und fwrite schreibt Daten in die datei.



  • Danke für die schnellen Anworten.
    Aber es löscht doch die vorhandende Datei und legt eine neue Datei an, oder nicht ?
    Zitat aus den Buch

    "w+" = Anlegen einer Datei zum Ändern. Existiert eine Datei mit gleichem Namen, wird diese zuvor gelöscht. Bei Fehlern oder mangelnden Rechten liefert fopen() hier NULL zurück.

    MfG
    Tom



  • Nice Catch! 🙂

    Ja, nach w+b ist das File leer.

    Deine letzte Instanz um solche Fragen zu beantworten ist der Standard:
    http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

    Hier z.B. 7.21.5.3 The fopen function.



  • HalloC schrieb:

    Ich habe dieses Beispiel aus den Buch "C von A bis Z" .

    Wundert mich, dass noch niemand was dazu gesagt hat:
    https://www.c-plusplus.net/forum/272350



  • von daher stimmt es ja auch, dass das Ende der Datei auch der Anfang ist und size müsste dann ja auch 0 sein🙄

    Oh mann!

    Ich kann dir c/c++ von Ulrich Kaiser empfehlen



  • Wie schon erwähnt: "w+[b]": Opens an empty file for both reading and writing. If the file exists, its contents are destroyed.
    Damit hast du unabhängig von deiner zuvor evtl. existierenden Datei test.txt eine neue 0 Byte lange test.txt. Du brauchst "r+b".

    Da deine Datei bisher also immer leer war, ist es auch noch nicht aufgefallen, dass du für fwrite mit (char *)'\0', also 0, eine ungültige Bufferadresse übergibst, was i.d.R. zum Crash führt. Du brauchst in der jetzigen Form einen mit 0-Bytes initialisierten Buffer in der Größe der Dateilänge, oder auch, bei einem kleineren Buffer, eine Schleife, die der Dateilänge entsprechend viele 0-Bytes schreibt.

    Ausserdem sollte bei allen Funktionen, die einen Returncode zurückgeben, dieser grundsätzlich auch geprüft werden. Sich in deinem Fall am Ende einfach mit einem EXIT_SUCCESS zu verabschieden, ist nicht unbedingt hilfreich.

    Anzumerken bleibt noch, dass du dich nicht darauf verlassen kannst, dass deine Datei vor dem Löschen wirklich überschrieben wird. Die Daten könnten letztlich auf deiner Platte immer noch vorhanden sein, wenn dein Betriebssystem feststellt, dass der Schreibvorgang überflüssig ist, weil die Datei anschliessend gelöscht wird. Ein tatsächliches Überschreiben ist möglicherweise nur mit zusätztlichem Aufwand zu erreichen.



  • Nochmal Danke an Alle.
    Der Dumme bin aber auch wirklich ich. Als ich nach diesen Beispiel mit den Buch weitergemacht.

    /* ptrptr3.c */
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) {
       char *sort[] = {
          "Zeppelin", "Auto", "Amerika", "Programmieren"
        };
       printf("%p = %c\n", **sort, **sort);
    
       printf("%p = %c\n", *sort[0], *sort[0]);
       printf("%p = %c\n", *(sort[0]+0), *(sort[0]+0));
       printf("%p = %s\n", sort[0], sort[0]);
       printf("%p = %s\n", *sort, *sort);
    
       printf("%p = %s\n", (sort[0]+1), (sort[0]+1));
       printf("%p = %s\n", (sort[0]+2), (sort[0]+2));
    
       printf("*sort = %p, **sort = %p\n", *sort, **sort);
       return EXIT_SUCCESS;
    }
    

    Es wird wohl Zeit für ein Literaturwechsel 🙄
    MfG
    Tom



  • Es fehlt auch noch ein fseek vor dem fwrite, um wieder auf den Anfang der Datei zu positionieren.



  • Schon die erste Zeile mit "shreeder.c" statt "shredder.c" in diesem völlig vermurksten Beispielcode, der sich mit all den Fehlern tatsächlich genauso im Netz findet, läßt erahnen, dass sich da ein Ober-Pfuscher in seinem Buch wohl so einiges zurechtgestümpert hat, ohne auch nur ansatzweise mal einen Test durchzuführen.

    Man könnnte ja, um das Überschreiben der Datei zu testen, den remove-Aufruf vorläufig auskommentieren, das Programm ausführen und die überschriebene test.txt mit einer Kopie der ursprünglichen test.txt vergleichen. Dann würde man vermutlich feststellen, dass der Dateiinhalt immer noch unverändert ist.



  • Mein Englisch ist nicht so gut, deshalb an die die den Standard besser verstehen.

    Furble Wurble schrieb:

    Nice Catch! 🙂

    Ja, nach w+b ist das File leer.

    Deine letzte Instanz um solche Fragen zu beantworten ist der Standard:
    http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

    Hier z.B. 7.21.5.3 The fopen function.

    Also da steht:
    w+b or wb+ truncate to zero lenght or create a binary file for update

    Wenn ich da weiter ein paar Zeilen weiter unten unter pos 7 nachsehe hat sorgt das "+" dafür, das da noch "besondere" Vorkehrungen für die Funktion erforderlich sind?

    Also das update bedeutet nicht wie in der Zeile zuvor:
    r+b Datei öffnen für update (lesen und schreiben)
    sondern eine Sonderbehandlung damit sich der Stream in einem definierten Zustand befindet?



  • "r" : Öffnen zum Lesen. Datei muss vorhanden sein.
    "r+" : Öffnen zum Lesen und Schreiben. Datei muss vorhanden sein.
    "w" : Öffnen zum Schreiben. Datei wird neu erzeugt und ist leer (falls vorher bereits vorhanden: Inhalt geht verloren).
    "w+" : Öffnen zum Lesen und Schreiben. Datei wird neu erzeugt und ist leer (falls vorher bereits vorhanden: Inhalt geht verloren).

    Mit zusätzlichem 'b' wird in allen Fällen der Binär-Modus festgelegt (keine Sonderbehandlung von Zeilentrennzeichen).



  • HalloC schrieb:

    fwrite((char *)'\0', 1, size, rem);
    

    Diese Zeile ist ja mal großer Müll, genauso wie der Rest vom Programm..

    Gar nicht weiter darüber nachdenken, aus dem Hirn löschen.



  • Hier mal etwas ausführlicher zum Nachvollziehen, wie man testen könnte, um den ganzen Ungereimtheiten auf die Schliche zu kommen:

    - Ich führe das Orginal-Programm aus und stelle fest, dass danach die test.txt nicht mehr vorhanden ist (soweit O.K.).
    - Ich möchte mich vergewissern, dass das Überschreiben wirklich funktioniert, und kommentiere die remove(rm)-Zeile aus.
    - Ich führe das geänderte Programm aus und stelle fest, dass die test.txt immer leer ist (Länge 0 Byte).
    - Ich stelle fest (Debugger), dass fopen() hierfür verantwortlich ist und bemerke, dass für mode "r+b" statt "w+b" übergeben werden muss.
    - Ich führe das geänderte Programm aus und es crasht.
    - Ich stelle fest (Debugger), dass die Bufferadresse bei fwrite() NULL ist. Ich behelfe mich damit, dass ich bei fwrite() die Adresse eines gültigen Buffers mit vorerst nur einem 0-Byte übergebe [(char 😉 ""] und den Count-Parameter auf 1 (statt "size") setze.
    - Ich führe das geänderte Programm aus und stelle fest, dass die Datei test.txt um ein 0-Byte am Ende größer wird, statt dass das erste Byte überschrieben wird.
    - Ich schließe auf eine aktuell falsche Datei-Position und stelle fest, dass vor dem Schreiben mit fwrite mittels Einfügen von fseek(rem, 0L, SEEK_SET) wieder auf den Anfang postioniert werden muss.
    - Ich führe das geänderte Programm aus und es wird wie erwartet das 1. Byte von test.txt mit einem 0-Byte überschrieben.

    Damit wären die gröbsten Bugs hoffentlich ausgemerzt. Jetzt muss noch folgendes erledigt werden:
    - Das Überschreiben auf alle Bytes der Datei ausdehnen (für entsprechenden Buffer sorgen / Schleife).
    - remove(rm) wieder aktivieren.

    Jetzt kannst du zur Übung vielleicht noch einige Erweiterungen vornehmen:
    - Returncodes auswerten (fseek, ftell, fwrite, fclose, remove) sowie my_remove() mit Returncode ausstatten, der in main() ausgewertet werden kann.
    - Kommadozeilenparameter für Dateiname, Löschzeichen, evtl. Option für Löschen oder Beibehalten (zwecks Test) der Datei nach dem Überschreiben.
    - Ausgabe von Fehler-/Erfolgsmeldung.



  • my_remove schrieb:

    😋 😡 Du brauchst in der jetzigen Form einen mit 0-Bytes initialisierten Buffer in der Größe der Dateilänge, oder auch, bei einem kleineren Buffer, eine Schleife, die der Dateilänge entsprechend viele 0-Bytes schreibt.

    Der erste Vorschlag ist gut, der zweite eher nicht so, weil alles, was mit lesen und schreiben bzw. IO zu tun hat, unglaublich langsam ist wegen Umschaltung auf kernelmode, schreibkopf positionieren usw.
    Daher besser alles in einen Buffer und mit einem Rutsch schreiben.



  • HansKlaus schrieb:

    my_remove schrieb:

    😋 😡 Du brauchst in der jetzigen Form einen mit 0-Bytes initialisierten Buffer in der Größe der Dateilänge, oder auch, bei einem kleineren Buffer, eine Schleife, die der Dateilänge entsprechend viele 0-Bytes schreibt.

    Der erste Vorschlag ist gut, der zweite eher nicht so, weil alles, was mit lesen und schreiben bzw. IO zu tun hat, unglaublich langsam ist wegen Umschaltung auf kernelmode, schreibkopf positionieren usw.
    Daher besser alles in einen Buffer und mit einem Rutsch schreiben.

    Du kannst dich auf Pufferung der crt und des os verlassen, das Rad neuerfinden ist nicht nötig.



  • naja aber allein dadurch, dass ich bei einer schleife geschätzte 10mio schleifenaufrufe im sinne von "schreibe das zeichen an der adresse x nach datei" (also nochmal zig zugriffe auf den speicher) habe, während es bei nem puffer vielleicht nur 10000 aufrufe im sinne von "schreibe alle zeichen in dem array an adresse x nach datei" sind.

    jedenfalls war es mal eine laboraufgabe, genau dieses problem zu untersuchen.



  • HansKlaus schrieb:

    ... während es bei nem puffer vielleicht nur 10000 aufrufe im sinne von "schreibe alle zeichen in dem array an adresse x nach datei" sind. ...

    Das ist aber genau das, was mein, wie du sagstest, 2. nicht so guter Vorschlag, beinhaltet, nämlich diesen "kleineren Buffer", der ja nicht zwingend nur ein Byte groß sein muss, aber bei einer Datei von erheblicher Größe bekomme ich möglicherweise keinen Buffer in der Größe der Dateilänge, wodurch wiederholtes Schreiben über eine geeignete Schleifenkonstruktion erforderlich wird.

    In meiner Testumgebung war übrigens festzustellen, dass bei fwrite intern ein 4K grosser Buffer ins Spiel kommt.


Log in to reply