Wie funktioniert Fread wirklich?

Lesezeit: 8 Minuten

Benutzeravatar von Roman Byshko
Roman Byschko

Die Erklärung von fread ist wie folgt:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

Die Frage ist: Gibt es einen Unterschied in der Leseleistung von zwei solchen Anrufen? fread:

char a[1000];
  1. fread(a, 1, 1000, stdin);
  2. fread(a, 1000, 1, stdin);

Wird es gelesen 1000 Bytes auf einmal jedes Mal?

Benutzeravatar von Keith Thompson
Keith Thompson

Es kann einen Unterschied in der Leistung geben oder auch nicht. Es gibt einen Unterschied in der Semantik.

fread(a, 1, 1000, stdin);

versucht, 1000 Datenelemente zu lesen, von denen jedes 1 Byte lang ist.

fread(a, 1000, 1, stdin);

versucht, 1 Datenelement zu lesen, das 1000 Bytes lang ist.

Sie sind anders, weil fread() gibt die Anzahl der Datenelemente zurück, die gelesen werden konnten, nicht die Anzahl der Bytes. Wenn es das Dateiende (oder eine Fehlerbedingung) erreicht, bevor es die vollen 1000 Bytes gelesen hat, muss die erste Version genau angeben, wie viele Bytes es gelesen hat; die zweite schlägt einfach fehl und gibt 0 zurück.

In der Praxis wird wahrscheinlich nur eine untergeordnete Funktion aufgerufen, die versucht, 1000 Bytes zu lesen, und angibt, wie viele Bytes tatsächlich gelesen wurden. Bei größeren Lesevorgängen werden möglicherweise mehrere Aufrufe auf niedrigerer Ebene ausgeführt. Die Berechnung des zurückzugebenden Werts von fread() ist anders, aber der Rechenaufwand ist trivial.

Es kann einen Unterschied geben, ob die Implementierung vor dem Versuch, die Daten zu lesen, feststellen kann, dass nicht genügend Daten zum Lesen vorhanden sind. Wenn Sie beispielsweise aus einer 900-Byte-Datei lesen, liest die erste Version alle 900 Bytes und gibt 900 zurück, während die zweite Version sich möglicherweise nicht die Mühe macht, etwas zu lesen. In beiden Fällen wird die Dateipositionsanzeige um die Zahl von erhöht Figuren erfolgreich gelesen, dh 900.

Aber im Allgemeinen sollten Sie wahrscheinlich wählen, wie Sie es aufrufen, basierend auf den Informationen, die Sie daraus benötigen. Lesen Sie ein einzelnes Datenelement, wenn ein teilweises Lesen nicht besser ist, als gar nichts zu lesen. Lesen Sie in kleineren Teilen, wenn partielle Lesevorgänge nützlich sind.

  • der zweite mag sich nicht die Mühe machen, etwas zu lesen. In beiden Fällen wird die Dateipositionsanzeige um die Anzahl der erfolgreich gelesenen Zeichen erhöht, dh 900 sollte es nicht sein, dass in der zweiten Version die Dateipositionsanzeige würde nicht vorab da nichts gelesen wurde? Mit anderen Worten, sollte nicht fread(a, 1000, N, stdin); Stellen Sie die fp-Anzeige immer um ein Vielfaches von vor 1000?

    – Shahbaz

    27. Januar 2014 um 11:26 Uhr

  • Egal, gefunden. C11 bei 7.21.8.1.2 und 7.21.8.2.2 sagt: Wenn ein Fehler auftritt, ist der resultierende Wert des Dateipositionsindikators für den Stream unbestimmt.

    – Shahbaz

    27. Januar 2014 um 11:27 Uhr


  • Es gibt also keine Möglichkeit, die Position des Indikators wiederherzustellen? Oder um zu vermeiden, den letzten Brocken zu lesen, der die Positionsanzeige durcheinander bringt?

    – David 天宇 Wong

    8. Februar 2014 um 16:01 Uhr

  • @David天宇Wong: Wenn Sie die Position wiedererlangen müssen, rufen Sie an ftell vor dem Anruf freadund dann fseek nach.

    – Keith Thompson

    8. Februar 2014 um 16:02 Uhr

  • Die POSIX-Spezifikation ist viel strenger … sie erfordert, dass fread die Größe von fgetcs pro Objekt bestimmt, sodass in beiden Fällen genau dieselbe Anzahl von fgetcs ausgeführt wird (aber die Rückgabewerte sind unterschiedlich).

    – Jim Balter

    15. August 2014 um 6:34 Uhr

Benutzeravatar von ArjunShankar
ArjunShankar

Entsprechend die Spezifikationkönnen die beiden von der Implementierung unterschiedlich behandelt werden.

Wenn Ihre Datei kleiner als 1000 Byte ist, fread(a, 1, 1000, stdin) (Lesen von 1000 Elementen mit jeweils 1 Byte) kopiert weiterhin alle Bytes bis EOF. Andererseits ist das Ergebnis von fread(a, 1000, 1, stdin) (Lesen Sie 1 1000-Byte-Element) gespeichert in a ist nicht angegeben, da nicht genügend Daten vorhanden sind, um das Lesen des „ersten“ (und einzigen) 1000-Byte-Elements abzuschließen.

Natürlich können einige Implementierungen das „partielle“ Element immer noch in so viele Bytes wie nötig kopieren.

Benutzeravatar von kennytm
kennytm

Das wäre das Implementierungsdetail. In glibc sind die beiden in der Leistung identisch, da sie im Grunde wie folgt implementiert sind (Ref http://sourceware.org/git/?p=glibc.git;a=blob;f=libio/iofread.c):

size_t fread (void* buf, size_t size, size_t count, FILE* f)
{
    size_t bytes_requested = size * count;
    size_t bytes_read = read(f->fd, buf, bytes_requested);
    return bytes_read / size;
}

Beachten Sie, dass C und POSIX Standard garantiert keine vollständige Objektgröße size muss jedes Mal gelesen werden. Wenn ein vollständiges Objekt nicht gelesen werden kann (z stdin hat nur 999 Bytes, aber Sie haben angefordert size == 1000), wird die Datei in einem unbestimmten Zustand belassen (C99 §7.19.8.1/2).

Bearbeiten: Siehe die anderen Antworten zu POSIX.

  • Sie erwähnen den POSIX-Standard, aber er erfordert, dass fread in Bezug auf fgetc implementiert wird, was viel deterministischer ist als die C-Anforderung.

    – Jim Balter

    15. August 2014 um 6:36 Uhr


  • Tolle Antwort..!! genau das, was jeder braucht, der hier landet..!!! Ich bin überrascht, dass es so viele Stimmen hat.

    – Sandeep

    3. September 2014 um 8:30 Uhr


  • Gilt das auch für fwrite?

    – Sandeep

    3. September 2014 um 8:35 Uhr

  • Wichtiger Punkt: Sie können die Datei beschädigen, wenn Sie Datensätze mit einer Größe von >1 lesen.

    – ArekBulski

    27. Oktober 2015 um 3:38 Uhr

  • @kennythm Tut es nicht read kann vorher mehrmals angerufen werden fread kehrt zurück, um die Anforderung des Anrufers zu erfüllen, der dies möglicherweise möchte fread 1 MB Byte?

    – John

    1. Juni um 9:34

Benutzeravatar von Neel Basu
Neel Basu

fread Anrufe getc im Inneren. in Minix Anzahl getc heißt ist einfach size*nmemb also wie oft getc angerufen wird, hängt von der ab Produkt von diesen beiden. Also beides fread(a, 1, 1000, stdin) und fread(a, 1000, 1, stdin) werde rennen getc 1000=(1000*1) Mal. Hier ist die einfache Implementierung von fread von Minix

size_t fread(void *ptr, size_t size, size_t nmemb, register FILE *stream){
register char *cp = ptr;
register int c;
size_t ndone = 0;
register size_t s;

if (size)
    while ( ndone < nmemb ) {
    s = size;
    do {
        if ((c = getc(stream)) != EOF)
            *cp++ = c;
        else
            return ndone;
    } while (--s);
    ndone++;
}

return ndone;
}

Möglicherweise gibt es keinen Leistungsunterschied, aber diese Aufrufe sind nicht identisch.

  • fread gibt die Anzahl der gelesenen Elemente zurück, sodass diese Aufrufe unterschiedliche Werte zurückgeben.
  • Wenn ein Element nicht vollständig gelesen werden kann, ist sein Wert unbestimmt:

Wenn ein Fehler auftritt, ist der resultierende Wert des Dateipositionsindikators für den Stream unbestimmt. Wenn ein Teilelement gelesen wird, ist sein Wert unbestimmt. (ISO/IEC 9899:TC2 7.19.8.1)

Es gibt keinen großen Unterschied in der glibc-Implementierung, das einfach die Elementgröße mit der Anzahl der Elemente multipliziert, um zu bestimmen, wie viele Bytes gelesen werden sollen, und die gelesene Menge am Ende durch die Elementgröße dividiert. Aber die Version, die eine Elementgröße von 1 angibt, wird Ihnen immer die richtige Anzahl gelesener Bytes mitteilen. Wenn Sie jedoch nur auf vollständig gelesene Elemente einer bestimmten Größe Wert legen, erspart Ihnen die Verwendung der anderen Form eine Division.

Benutzeravatar von Jeegar Patel
Jeegar Patel

Noch eine Satzform http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html es ist nicht möglich

Die fread()-Funktion liest in das Array, auf das ptr zeigt, bis zu nitems Elementen, deren Größe durch size in Bytes angegeben ist, aus dem Stream, auf den stream zeigt. Für jedes Objekt sollen Größenaufrufe an die Funktion fgetc() erfolgen und die Ergebnisse gespeichert werdenin der gelesenen Reihenfolge, in einem Array von unsigned char, das genau das Objekt überlagert.

Kurz gesagt, in beiden Fällen wird mit fgetc() auf die Daten zugegriffen…!

Ich wollte die Antworten hier verdeutlichen. fread führt gepufferte IO durch. Die tatsächlichen Leseblockgrößen, die fread verwendet, werden durch die verwendete C-Implementierung bestimmt.

Alle modernen C-Bibliotheken haben die gleiche Leistung mit den beiden Aufrufen:

fread(a, 1, 1000, file);
fread(a, 1000, 1, file);

Sogar sowas wie:

for (int i=0; i<1000; i++)
  a[i] = fgetc(file)

Sollte zu den gleichen Festplattenzugriffsmustern führen, obwohl fgetc aufgrund von mehr Aufrufen in die Standard-c-Bibliotheken und in einigen Fällen der Notwendigkeit einer Festplatte zur Durchführung zusätzlicher Suchvorgänge langsamer wäre, die andernfalls wegoptimiert worden wären.

Zurück zum Unterschied zwischen den beiden Formen von Fread. Ersteres gibt die tatsächliche Anzahl gelesener Bytes zurück. Letzteres gibt 0 zurück, wenn die Dateigröße kleiner als 1000 ist, ansonsten 1. In beiden Fällen würde der Puffer mit den gleichen Daten gefüllt werden, dh dem Inhalt der Datei bis zu 1000 Bytes.

Im Allgemeinen möchten Sie wahrscheinlich den 2. Parameter (Größe) auf 1 gesetzt lassen, damit Sie die Anzahl der gelesenen Bytes erhalten.

  • “Alle modernen C-Bibliotheken werden mit den beiden Aufrufen die gleiche Leistung haben” – ja. “in einigen Fällen die Notwendigkeit einer Festplatte, um zusätzliche Suchvorgänge durchzuführen, die andernfalls wegoptimiert worden wären” — nein. fgetc liest einfach aus dem speicherinternen Puffer von stdio. Und selbst wenn der Stream auf ungepuffert eingestellt wurde, puffert das zugrunde liegende Betriebssystem die Lesevorgänge von der Festplatte.

    – Jim Balter

    15. August 2014 um 6:46 Uhr

  • @Jim: fgetc liest von stdio anders als fread. Das offensichtliche Ergebnis davon ist, dass fgetc immer die Anzahl der Suchvorgänge/Systemaufrufe (schlecht) maximiert, während fread die Anzahl der Suchvorgänge/Systemaufrufe minimiert, da Sie libc mehr Informationen darüber geben, was Sie tun.

    – Klarus

    16. August 2014 um 0:49 Uhr

  • Entschuldigung, aber Sie haben keine Ahnung, wovon Sie sprechen … es gibt keinen Unterschied zwischen fread und fgetc, der sich auf die Anzahl der Suchvorgänge auswirkt, und Sie haben keine Unterstützung für diese absurde Behauptung geliefert. Beachten Sie, dass die Definition von fread in den C99- und POSIX-Standards in Form von fgetc angegeben wird, wie an anderer Stelle auf dieser Seite besprochen.

    – Jim Balter

    16. August 2014 um 2:22 Uhr


1418120cookie-checkWie funktioniert Fread wirklich?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy