Ich möchte den gesamten Inhalt einer Datei in einen Puffer schreiben. Die Datei enthält eigentlich nur einen String, den ich mit einem String vergleichen muss.
Was wäre die effizienteste Option, die auch unter Linux portabel ist.
ENV: Windows
Ich möchte den gesamten Inhalt einer Datei in einen Puffer schreiben. Die Datei enthält eigentlich nur einen String, den ich mit einem String vergleichen muss.
Was wäre die effizienteste Option, die auch unter Linux portabel ist.
ENV: Windows
Die Portierbarkeit zwischen Linux und Windows ist ein großes Problem, da Linux ein POSIX-konformes System mit – im Allgemeinen – einer richtigen, hochwertigen Toolchain für C ist, während Windows nicht einmal viele Funktionen in der C-Standardbibliothek bereitstellt.
Wenn Sie sich jedoch an den Standard halten möchten, können Sie Folgendes schreiben:
#include <stdio.h>
#include <stdlib.h>
FILE *f = fopen("textfile.txt", "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET); /* same as rewind(f); */
char *string = malloc(fsize + 1);
fread(string, fsize, 1, f);
fclose(f);
string[fsize] = 0;
Hier string
enthält den Inhalt der Textdatei als ordnungsgemäß mit 0 abgeschlossenen C-String. Dieser Code ist nur Standard-C, er ist nicht POSIX-spezifisch (obwohl er nicht garantiert, dass er unter Windows funktioniert/kompiliert wird …)
Nur für den Fall, dass sich Besucher fragen, rewind(f);
ist äquivalent zu fseek(f, 0, SEEK_SET);
und könnte stattdessen hier verwendet werden. Beides gehört dazu <stdio.h>
– Lynchen
25. April 2013 um 11:45 Uhr
Ach ja, und bevor es darauf ankommt: man muss immer den Rückgabewert prüfen malloc()
und fread()
. Hier wird die Fehlerprüfung nur der Einfachheit halber weggelassen – kopieren Sie diesen Code nicht wörtlich in eine Produktionscodebasis.
– Benutzer529758
26. September 2013 um 10:23 Uhr
Vergiss es nicht free()
das string
.
– Tomáš Zato – Wiedereinsetzung von Monica
13. Mai 2014 um 12:08 Uhr
Tatsächlich tut diese Lösung nicht halte dich an den Standard; Der Standard schreibt vor, dass “ein Binärstrom keine sinnvolle Unterstützung braucht fseek
Aufrufe mit einem where-Wert von SEEK_END
“, und dass “die Dateipositionsanzeige auf das Dateiende setzen, wie bei fseek(file, 0, SEEK_END)
hat ein undefiniertes Verhalten für einen binären Stream”.
– Ori
5. März 2016 um 0:46 Uhr
Vielleicht fsize = fread(string, 1, fsize, f); wäre besser – falls es nicht ganz gelesen ist.
– android.weasel
26. April 2016 um 14:23 Uhr
Hier ist, was ich empfehlen würde.
Es sollte C89-konform und vollständig portabel sein. Insbesondere funktioniert es auch auf Pipes und Sockets auf POSIXy-Systemen.
Die Idee ist, dass wir die Eingabe in großen Stücken lesen (READALL_CHUNK
), den Puffer dynamisch nach Bedarf neu zuordnen. Wir verwenden nur realloc()
, fread()
, ferror()
und free()
:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
/* Size of each input chunk to be
read and allocate for. */
#ifndef READALL_CHUNK
#define READALL_CHUNK 262144
#endif
#define READALL_OK 0 /* Success */
#define READALL_INVALID -1 /* Invalid parameters */
#define READALL_ERROR -2 /* Stream error */
#define READALL_TOOMUCH -3 /* Too much input */
#define READALL_NOMEM -4 /* Out of memory */
/* This function returns one of the READALL_ constants above.
If the return value is zero == READALL_OK, then:
(*dataptr) points to a dynamically allocated buffer, with
(*sizeptr) chars read from the file.
The buffer is allocated for one extra char, which is NUL,
and automatically appended after the data.
Initial values of (*dataptr) and (*sizeptr) are ignored.
*/
int readall(FILE *in, char **dataptr, size_t *sizeptr)
{
char *data = NULL, *temp;
size_t size = 0;
size_t used = 0;
size_t n;
/* None of the parameters can be NULL. */
if (in == NULL || dataptr == NULL || sizeptr == NULL)
return READALL_INVALID;
/* A read error already occurred? */
if (ferror(in))
return READALL_ERROR;
while (1) {
if (used + READALL_CHUNK + 1 > size) {
size = used + READALL_CHUNK + 1;
/* Overflow check. Some ANSI C compilers
may optimize this away, though. */
if (size <= used) {
free(data);
return READALL_TOOMUCH;
}
temp = realloc(data, size);
if (temp == NULL) {
free(data);
return READALL_NOMEM;
}
data = temp;
}
n = fread(data + used, 1, READALL_CHUNK, in);
if (n == 0)
break;
used += n;
}
if (ferror(in)) {
free(data);
return READALL_ERROR;
}
temp = realloc(data, used + 1);
if (temp == NULL) {
free(data);
return READALL_NOMEM;
}
data = temp;
data[used] = '\0';
*dataptr = data;
*sizeptr = used;
return READALL_OK;
}
Oben habe ich eine konstante Chunk-Größe verwendet, READALL_CHUNK
== 262144 (256*1024
). Das bedeutet, dass im schlimmsten Fall bis zu 262145 Zeichen verschwendet werden (allokiert, aber nicht verwendet), aber nur vorübergehend. Am Ende weist die Funktion den Puffer auf die optimale Größe neu zu. Das bedeutet auch, dass wir vier Neuzuweisungen pro Megabyte gelesener Daten vornehmen.
Der Standardwert von 262144 Byte im obigen Code ist ein konservativer Wert; Es funktioniert gut für sogar alte Minilaptops und Raspberry Pis und die meisten eingebetteten Geräte mit mindestens einigen Megabyte RAM, die für den Prozess verfügbar sind. Es ist jedoch nicht so klein, dass es die Operation (aufgrund vieler Leseaufrufe und vieler Pufferneuzuweisungen) auf den meisten Systemen verlangsamt.
Für Desktop-Rechner zu diesem Zeitpunkt (2017) empfehle ich einen viel größeren READALL_CHUNK
vielleicht #define READALL_CHUNK 2097152
(2 MiB).
Denn die Definition von READALL_CHUNK
geschützt ist (d. h. es ist nur definiert, wenn es zu diesem Zeitpunkt im Code noch undefiniert ist), können Sie den Standardwert zur Kompilierzeit überschreiben, indem Sie (in den meisten C-Compilern) verwenden -DREADALL_CHUNK=2097152
Befehlszeilenoption — aber überprüfen Sie Ihre Compileroptionen für die Definition eines Präprozessormakros mit Befehlszeilenoptionen.
Stimmen Sie zu, dass Sie die Datei nicht gesucht haben
– Elvis Strazdins
25. April 2018 um 14:59 Uhr
@ElvissStrazdins: Danke; Das ist genau der Grund, warum ich erwähnt habe, dass dieser für Rohre und Steckdosen funktioniert – sie sind überhaupt nicht suchbar. Haben Sie eine Meinung, ob ich einen Absatz darüber hinzufügen sollte, wie der Seek-Ansatz bei diesen nicht funktioniert? (Die auch nicht fstat()
übrigens.) Das Lesen des Streams, bis das Lesen fehlschlägt, ist wirklich die einzige portable Option, die mit allem funktioniert, was Sie bekommen können FILE
anfassen. Ich würde es vorziehen, wenn neue C-Programmierer das wissen, bevor es sie in den Knöchel beißt, wissen Sie.
– Nominelles Tier
13. August 2018 um 14:30 Uhr
Nicht nur Pipes und Sockets sind das Problem, sondern das Abrufen der Dateigröße mit fstat und das anschließende Lesen der Datei führt zu einer Race-Condition, falls die Datei extern geändert wird (Daten in einem anderen Prozess hinzufügen oder daraus löschen).
– Elvis Strazdins
14. August 2018 um 0:57 Uhr
@ElvissStrazdins: Sehr wahr. Doch fast alle Antworten auf diese und ähnliche Fragen verwenden die Seek-Methode. Ebenso sollte man verwenden nftw()
/fts_..()
/glob()
/wordexp()
statt opendir()
/readdir()
/closedir()
, um Dateien/Verzeichnisse, die während des Durchlaufs hinzugefügt/gelöscht/umbenannt werden, einfach zu handhaben. Ich weiß, dass es mir egal sein sollte, aber ich mag die Idee wirklich nicht, dass mehr C-Programmierer Code schreiben, der nur unter bestimmten Umständen funktioniert und ansonsten stillschweigend fehlschlägt – oder noch schlimmer, Daten zerstört. Die Welt ist bereits voll von solchem Code, und wir brauchen weniger davon, nicht mehr.
– Nominelles Tier
14. August 2018 um 1:31 Uhr
@Andreas: Der Overhead eines realloc()- und eines read()-Systemaufrufs ist für größere Chunk-Größen (2 MiB oder mehr auf derzeit typischen Desktop-Computern) unbedeutend, sodass die Operation E / A-gebunden ist und die Zeitkomplexität irrelevant ist. die benötigte Zeit ist im Wesentlichen eine lineare Funktion der (großen) Dateigröße. Es ist besser, stattdessen die Menge des zugewiesenen, aber ungenutzten Speichers zu begrenzen.
– Nominelles Tier
13. September 2018 um 16:10 Uhr
Eine tragbare Lösung könnte verwendet werden getc
.
#include <stdio.h>
char buffer[MAX_FILE_SIZE];
size_t i;
for (i = 0; i < MAX_FILE_SIZE; ++i)
{
int c = getc(fp);
if (c == EOF)
{
buffer[i] = 0x00;
break;
}
buffer[i] = c;
}
Wenn Sie keine haben möchten MAX_FILE_SIZE
Makro oder wenn es sich um eine große Zahl handelt (z buffer
wäre zu groß, um auf den Stapel zu passen), verwenden Sie die dynamische Zuordnung.
Ordnen Sie dem Heap besser riesige Speicherblöcke zu. Bitte lesen Sie auch nicht Byte für Byte, ich bin mir sicher, dass in jeder anständigen Implementierung von libc die fread()
Funktion bietet etwas Effizienteres.
– Benutzer529758
22. Dezember 2012 um 13:21 Uhr
@H2CO3: Ich stimme der Tatsache vollkommen zu, dass das Lesen von Byte pro Byte ineffizient ist, es ging nur darum, eine standardmäßige und sehr einfache Lösung bereitzustellen (fgets
könnte auch klappen). Außerdem verwende ich keine POSIX-Funktionen wie z fread
unter Windows, da die POSIX-Implementierung durch dieses Betriebssystem oft von den Spezifikationen abweicht. Über die Heap-Zuweisung steht an diesem Ende meiner Antwort.
– md5
22. Dezember 2012 um 13:25 Uhr
fread()
ist nicht POSIX-spezifisch. Wenn Sie es nicht verwenden möchten, können Sie es aufgeben fgetc()
auch.
– Benutzer529758
22. Dezember 2012 um 13:29 Uhr
“das sogar unter Linux portabel ist” sollte “das sogar unter Windows portabel ist” sein …
– Benutzer529758
22. Dezember 2012 um 12:47 Uhr
Ich schreibe Code unter Windows und möchte auch auf Linux portieren
– Sonnig
22. Dezember 2012 um 12:48 Uhr
Also, was hast du versucht?
– Mats Petersson
22. Dezember 2012 um 12:49 Uhr
Sie müssen die Datei nicht in einen Puffer einlesen, um sie mit einer Zeichenfolge zu vergleichen. Es ist besser, es im laufenden Betrieb zu tun. 2. Achten Sie auf Codierungen. Unter Windows gibt es einige lächerliche beliebte Kodierungen, wie z. B. UTF-16.
– Pavel Radzivilovsky
22. Dezember 2012 um 12:57 Uhr