Korrekte Methode zum Einlesen einer Textdatei in einen Puffer in C? [duplicate]
Lesezeit: 6 Minuten
Ich habe es mit kleinen Textdateien zu tun, die ich in einen Puffer einlesen möchte, während ich sie verarbeite, also habe ich mir den folgenden Code ausgedacht:
Das ist falsch. strcat verkettet Strings. Selbst wenn &symbol ist ein char *, es ist nicht nullterminiert. Du solltest benutzen fgets oder fread. Ebenfalls, strcat wird in Ihrem Fall sowieso langsam sein, weil es scannt source jedes Mal muss ein Zeichen angehängt werden.
– Alok Singhal
8. Januar 2010 um 16:50 Uhr
Ganz zu schweigen davon, dass das Lesen eines Zeichens auf einmal viel langsamer ist als das Verwenden fread.
– Nik Meyer
8. Januar 2010 um 16:57 Uhr
@Nick: Ich weiß nichts über ‘viel langsamer’: Aufgrund der io-Pufferung und möglicherweise des Inlinings der Funktionsaufrufe muss die Auswirkung auf die Leistung nicht unbedingt so groß sein. verwenden fread() ist aber trotzdem eine gute idee
– Christoph
8. Januar 2010 um 17:07 Uhr
Übrigens: Ich würde eine 1m-Textdatei nicht als “klein” betrachten 😉
– Christoph
8. Januar 2010 um 17:09 Uhr
Sehen Sie in mmap() nach, um die Datei im Speicher abzubilden. Achten Sie auf Pufferüberläufe. Verwenden Sie nicht strcat() – selbst wenn Sie die Probleme mit Nullterminierung beheben, erhalten Sie ein quadratisches Verhalten, das bei Megabyte-Dateien schlecht und bei Gigabyte-Dateien eine Katastrophe ist.
Es ist sehr langsam (Sie extrahieren den Puffer Zeichen für Zeichen).
Wenn die Dateigröße überschritten ist sizeof(source)ist dies anfällig für Pufferüberläufe.
Wirklich, wenn Sie es genauer betrachten, sollte dieser Code überhaupt nicht funktionieren. Wie in den Manpages angegeben:
Das strcat() Die Funktion hängt eine Kopie des nullterminierten Strings s2 an das Ende des nullterminierten Strings s1 und fügt dann ein abschließendes `\0′ hinzu.
Sie hängen ein Zeichen (keine NUL-terminierte Zeichenfolge!) An eine Zeichenfolge an, die NUL-terminiert sein kann oder nicht. Das nur Zeitweise kann ich mir vorstellen, dass dies gemäß der Manpage-Beschreibung funktioniert, wenn jedes Zeichen in der Datei NUL-terminiert ist, in diesem Fall wäre dies ziemlich sinnlos. Also ja, das ist definitiv ein schrecklicher Missbrauch von strcat().
Im Folgenden finden Sie zwei Alternativen, die Sie stattdessen verwenden sollten.
Wenn Sie die maximale Puffergröße im Voraus kennen:
#include <stdio.h>
#include <stdlib.h>
char *source = NULL;
FILE *fp = fopen("foo.txt", "r");
if (fp != NULL) {
/* Go to the end of the file. */
if (fseek(fp, 0L, SEEK_END) == 0) {
/* Get the size of the file. */
long bufsize = ftell(fp);
if (bufsize == -1) { /* Error */ }
/* Allocate our buffer to that size. */
source = malloc(sizeof(char) * (bufsize + 1));
/* Go back to the start of the file. */
if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }
/* Read the entire file into memory. */
size_t newLen = fread(source, sizeof(char), bufsize, fp);
if ( ferror( fp ) != 0 ) {
fputs("Error reading file", stderr);
} else {
source[newLen++] = '\0'; /* Just to be safe. */
}
}
fclose(fp);
}
free(source); /* Don't forget to call free() later! */
Sie möchten wahrscheinlich auch Ihren Puffer mit Null terminieren. In Ihrem zweiten Codebeispiel haben Sie Platz für die Null gelassen, sie aber nicht wirklich festgelegt. In Ihrem ersten haben Sie es versäumt, Platz für die Null zu lassen.
– Brian Campell
8. Januar 2010 um 17:10 Uhr
+1 für die Verwendung von ftell und malloc. Dies ist der richtige Weg.
– Zigarrenmann
8. Januar 2010 um 17:25 Uhr
@ Mark: du hast recht, und ich bin sicher, du weißt das, aber sizeof(int) kann 1 sein. @Michael, sagen wir, ich lese ein Zeichen ‘A’ ein symbol. Dann, &symbol (selbst wenn symbol ist char) ist ein Zeiger, der auf zeigt 'A' gefolgt von zufälligen Daten. Wenn symbol ist ein intund sizeof(int) > 1dann &symbolwenn konvertiert in char *verweist auf 'A' gefolgt von 0 oder 0abhängig von der Endianness der Maschine.
– Alok Singhal
8. Januar 2010 um 18:49 Uhr
@Alok, Mark: Ich habe vergessen, dass getc() ein int zurückgibt, kein char. Wow, das ist sehr subtil.
– Michael
8. Januar 2010 um 21:55 Uhr
stackoverflow.com/a/238607/309483: “Wenn Sie ftell verwenden, müssen Sie die Datei im Binärmodus öffnen. Wenn Sie sie im Textmodus öffnen, gibt ftell nur ein “Cookie” zurück, das nur von fseek verwendet werden kann.”
– Janus Troelsen
18. Oktober 2012 um 22:16 Uhr
Martin Beckett
Ja – Sie würden wahrscheinlich wegen Ihres schrecklichen Missbrauchs von strcat verhaftet werden!
Schauen Sie sich getline() an, es liest die Daten zeilenweise, aber vor allem kann es die Anzahl der gelesenen Zeichen begrenzen, damit der Puffer nicht überläuft.
Strcat ist relativ langsam, weil es bei jeder Zeicheneinfügung den gesamten String nach dem Ende durchsuchen muss. Normalerweise würden Sie einen Zeiger auf das aktuelle Ende des Zeichenfolgenspeichers behalten und diesen an getline als Position übergeben, an der die nächste Zeile gelesen werden soll.
Wenn Sie sich auf einem Linux-System befinden, können Sie mit fstat() viele Informationen über die Datei erhalten, sobald Sie den Dateideskriptor haben.
Das ist falsch.
strcat
verkettet Strings. Selbst wenn&symbol
ist einchar *
, es ist nicht nullterminiert. Du solltest benutzenfgets
oderfread
. Ebenfalls,strcat
wird in Ihrem Fall sowieso langsam sein, weil es scanntsource
jedes Mal muss ein Zeichen angehängt werden.– Alok Singhal
8. Januar 2010 um 16:50 Uhr
Ganz zu schweigen davon, dass das Lesen eines Zeichens auf einmal viel langsamer ist als das Verwenden
fread
.– Nik Meyer
8. Januar 2010 um 16:57 Uhr
@Nick: Ich weiß nichts über ‘viel langsamer’: Aufgrund der io-Pufferung und möglicherweise des Inlinings der Funktionsaufrufe muss die Auswirkung auf die Leistung nicht unbedingt so groß sein. verwenden
fread()
ist aber trotzdem eine gute idee– Christoph
8. Januar 2010 um 17:07 Uhr
Übrigens: Ich würde eine 1m-Textdatei nicht als “klein” betrachten 😉
– Christoph
8. Januar 2010 um 17:09 Uhr
Sehen Sie in mmap() nach, um die Datei im Speicher abzubilden. Achten Sie auf Pufferüberläufe. Verwenden Sie nicht strcat() – selbst wenn Sie die Probleme mit Nullterminierung beheben, erhalten Sie ein quadratisches Verhalten, das bei Megabyte-Dateien schlecht und bei Gigabyte-Dateien eine Katastrophe ist.
– Jonathan Leffler
8. Januar 2010 um 17:45 Uhr