Wie liest man den Inhalt einer Datei in eine Zeichenfolge in C?

Lesezeit: 8 Minuten

Benutzeravatar von Chris Bunch
Chris Haufen

Was ist der einfachste Weg (am wenigsten fehleranfällig, am wenigsten Codezeilen, wie auch immer Sie es interpretieren möchten), um eine Datei in C zu öffnen und ihren Inhalt in einen String (char*, char[]wie auch immer)?

  • „einfachster Weg“ und „am wenigsten fehleranfällig“ sind oft Gegensätze.

    – Andy Lester

    6. Oktober 2008 um 14:37 Uhr

  • “einfachster Weg” und “am wenigsten fehleranfällig” sind eigentlich Synonyme für mich. Die Antwort in C# lautet beispielsweise string s = File.ReadAllText(filename);. Wie könnte das einfacher und fehleranfälliger sein?

    – Mark Lakata

    7. April 2014 um 19:54 Uhr

Ich neige dazu, einfach den gesamten Puffer als rohen Speicherblock in den Speicher zu laden und das Parsen selbst durchzuführen. Auf diese Weise habe ich die beste Kontrolle darüber, was die Standardbibliothek auf mehreren Plattformen tut.

Dies ist ein Stub, den ich dafür verwende. Sie können auch die Fehlercodes für fseek, ftell und fread überprüfen. (wegen der Übersichtlichkeit weggelassen).

char * buffer = 0;
long length;
FILE * f = fopen (filename, "rb");

if (f)
{
  fseek (f, 0, SEEK_END);
  length = ftell (f);
  fseek (f, 0, SEEK_SET);
  buffer = malloc (length);
  if (buffer)
  {
    fread (buffer, 1, length, f);
  }
  fclose (f);
}

if (buffer)
{
  // start to process your data / extract strings here...
}

  • Ich würde auch den Rückgabewert von fread überprüfen, da es aufgrund von Fehlern möglicherweise nicht die gesamte Datei liest und was nicht.

    – Freiraum

    6. Oktober 2008 um 14:45 Uhr

  • Wie rmeador sagte, wird fseek bei Dateien > 4 GB fehlschlagen.

    – KPexEA

    6. Oktober 2008 um 15:33 Uhr

  • WAHR. Bei großen Dateien nervt diese Lösung.

    – Nils Pipenbrinck

    6. Oktober 2008 um 15:52 Uhr

  • Da es sich hierbei um eine Landingpage handelt, möchte ich darauf hinweisen fread beendet Ihre Zeichenfolge nicht mit Null. Dies kann zu Problemen führen.

    – Iwan-k

    8. September 2014 um 18:36 Uhr

  • Wie @Manbroski sagte, muss der Puffer mit ‘\ 0’ beendet werden. Also ich würde wechseln buffer = malloc (length + 1); und nach fclose hinzufügen: buffer[length] = '\0'; (von Valgrind validiert)

    – Sojaholz

    28. Oktober 2016 um 8:37 Uhr


Benutzeravatar von Jeff Mc
Jeff Mc

Eine andere, leider stark vom Betriebssystem abhängige Lösung ist die Speicherzuordnung der Datei. Zu den Vorteilen gehören im Allgemeinen die Leseleistung und die geringere Speichernutzung, da die Anwendungsansicht und der Dateicache des Betriebssystems den physischen Speicher gemeinsam nutzen können.

POSIX-Code würde so aussehen:

int fd = open("filename", O_RDONLY);
int len = lseek(fd, 0, SEEK_END);
void *data = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);

Windows hingegen ist etwas kniffliger, und leider habe ich keinen Compiler zum Testen vor mir, aber die Funktionalität wird von bereitgestellt CreateFileMapping() und MapViewOfFile().

  • Vergessen Sie nicht, die Rückgabewerte dieser Systemaufrufe zu überprüfen!

    – Toby Speight

    28. Februar 2018 um 10:50 Uhr

  • muss off_t anstelle von int verwenden, wenn lseek() aufgerufen wird.

    – ivan.ukr

    12. Juli 2018 um 5:06 Uhr


  • Beachten Sie, dass diese Lösung vermieden werden sollte, wenn das Ziel darin besteht, den Inhalt einer Datei zu einem bestimmten Zeitpunkt stabil im Speicher zu erfassen, es sei denn, Sie sind sicher, dass die in den Speicher gelesene Datei während des Intervalls nicht von anderen Prozessen geändert wird über die die Karte verwendet wird. Sieh dir das an Post für mehr Informationen.

    – Benutzer001

    20. Mai 2019 um 5:56 Uhr


Benutzeravatar von dmityugov
dmitjugow

Wenn “seinen Inhalt in eine Zeichenfolge lesen” bedeutet, dass die Datei keine Zeichen mit dem Code 0 enthält, können Sie auch die Funktion getdelim() verwenden, die entweder einen Speicherblock akzeptiert und ihn bei Bedarf neu zuweist oder einfach den gesamten Puffer für zuweist Sie und liest die Datei hinein, bis sie auf ein bestimmtes Trennzeichen oder Dateiende trifft. Übergeben Sie einfach ‘\0’ als Trennzeichen, um die gesamte Datei zu lesen.

Diese Funktion ist in der GNU C Library verfügbar, http://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getdelim-994

Der Beispielcode könnte so einfach aussehen wie

char* buffer = NULL;
size_t len;
ssize_t bytes_read = getdelim( &buffer, &len, '\0', fp);
if ( bytes_read != -1) {
  /* Success, now the entire file is in the buffer */

  • Ich habe das schon einmal benutzt! Es funktioniert sehr gut, vorausgesetzt, die Datei, die Sie lesen, ist Text (enthält kein \0).

    – vergänglich

    6. Oktober 2008 um 16:34 Uhr

  • HÜBSCH! Erspart viele Probleme beim Einschlürfen ganzer Textdateien. Nun, wenn es eine ähnliche ultraeinfache Möglichkeit gäbe, einen binären Dateistrom bis EOF zu lesen, ohne dass ein Trennzeichen erforderlich wäre!

    – Antonius

    5. Januar 2017 um 3:05 Uhr


Jakes Benutzeravatar
Jake

Wenn Sie spezielle Dateien wie stdin oder eine Pipe lesen, können Sie die Dateigröße nicht vorher mit fstat ermitteln. Wenn Sie eine Binärdatei lesen, verliert fgets außerdem die Informationen zur Zeichenfolgengröße aufgrund eingebetteter ‘\0’-Zeichen. Der beste Weg, eine Datei zu lesen, ist die Verwendung von read und realloc:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main () {
    char buf[4096];
    ssize_t n;
    char *str = NULL;
    size_t len = 0;
    while (n = read(STDIN_FILENO, buf, sizeof buf)) {
        if (n < 0) {
            if (errno == EAGAIN)
                continue;
            perror("read");
            break;
        }
        str = realloc(str, len + n + 1);
        memcpy(str + len, buf, n);
        len += n;
        str[len] = '\0';
    }
    printf("%.*s\n", len, str);
    return 0;
}

Benutzeravatar von Joe Cool
Jo Cool

Hinweis: Dies ist eine Modifikation der oben akzeptierten Antwort.

Hier ist eine Möglichkeit, dies zu tun, komplett mit Fehlerprüfung.

Ich habe einen Größenprüfer hinzugefügt, der beendet wird, wenn die Datei größer als 1 GiB ist. Ich habe dies getan, weil das Programm die gesamte Datei in eine Zeichenfolge packt, die zu viel RAM verbrauchen und einen Computer zum Absturz bringen kann. Wenn Ihnen das jedoch egal ist, können Sie es einfach aus dem Code entfernen.

#include <stdio.h>
#include <stdlib.h>

#define FILE_OK 0
#define FILE_NOT_EXIST 1
#define FILE_TOO_LARGE 2
#define FILE_READ_ERROR 3

char * c_read_file(const char * f_name, int * err, size_t * f_size) {
    char * buffer;
    size_t length;
    FILE * f = fopen(f_name, "rb");
    size_t read_length;
    
    if (f) {
        fseek(f, 0, SEEK_END);
        length = ftell(f);
        fseek(f, 0, SEEK_SET);
        
        // 1 GiB; best not to load a whole large file in one string
        if (length > 1073741824) {
            *err = FILE_TOO_LARGE;
            
            return NULL;
        }
        
        buffer = (char *)malloc(length + 1);
        
        if (length) {
            read_length = fread(buffer, 1, length, f);
            
            if (length != read_length) {
                 free(buffer);
                 *err = FILE_READ_ERROR;

                 return NULL;
            }
        }
        
        fclose(f);
        
        *err = FILE_OK;
        buffer[length] = '\0';
        *f_size = length;
    }
    else {
        *err = FILE_NOT_EXIST;
        
        return NULL;
    }
    
    return buffer;
}

Und um auf Fehler zu prüfen:

int err;
size_t f_size;
char * f_data;

f_data = c_read_file("test.txt", &err, &f_size);

if (err) {
    // process error
}
else {
    // process data
    free(f_data);
}

  • Nur eine Frage: die buffer Sie haben mit zugeteilt malloc(length +1), wird nicht befreit. Ist das etwas, was der Verbraucher dieser Methode tun soll, oder besteht dafür keine Notwendigkeit? free() der zugewiesene Speicher?

    – Pablosprojekt

    17. September 2020 um 9:44 Uhr

  • wenn kein Fehler aufgetreten ist, free(f_data); aufgerufen werden soll. thks für den Hinweis

    – Joe Cool

    20. September 2020 um 0:23 Uhr

  • Du hast “auch” falsch geschrieben FILE_TO_LARGE

    – Benutzer11171

    2. September 2021 um 14:31 Uhr

Was ist der einfachste Weg (am wenigsten fehleranfällig, am wenigsten Codezeilen, wie auch immer Sie es interpretieren möchten), um eine Datei in C zu öffnen und ihren Inhalt in einen String einzulesen …?

Leider sind die Antworten auch nach Jahren fehleranfällig und vielen mangelt es an der richtigen Schnur Bildung und Fehlerprüfung.

#include <stdio.h>
#include <stdlib.h>

// Read the file into allocated memory.
// Return NULL on error.
char* readfile(FILE *f) {
  // f invalid? fseek() fail?
  if (f == NULL || fseek(f, 0, SEEK_END)) {
    return NULL;
  }

  long length = ftell(f);
  rewind(f);
  // Did ftell() fail?  Is the length too long?
  if (length == -1 || (unsigned long) length >= SIZE_MAX) {
    return NULL;
  }

  // Convert from long to size_t
  size_t ulength = (size_t) length;
  char *buffer = malloc(ulength + 1);
  // Allocation failed? Read incomplete?
  if (buffer == NULL || fread(buffer, 1, ulength, f) != ulength) {
    free(buffer);
    return NULL;
  }
  buffer[ulength] = '\0'; // Now buffer points to a string

  return buffer;
}

Beachten Sie, dass, wenn die Textdatei enthält Nullzeichen, enthalten die zugewiesenen Daten alle Dateidaten, die Zeichenfolge scheint jedoch kurz zu sein. Besserer Code würde auch die Längeninformationen zurückgeben, damit der Aufrufer damit umgehen kann.

char* readfile(FILE *f, size_t *ulength_ptr) {
  ...
  if (ulength_ptr) *ulength_ptr == *ulength;
  ...
} 

  • Nur eine Frage: die buffer Sie haben mit zugeteilt malloc(length +1), wird nicht befreit. Ist das etwas, was der Verbraucher dieser Methode tun soll, oder besteht dafür keine Notwendigkeit? free() der zugewiesene Speicher?

    – Pablosprojekt

    17. September 2020 um 9:44 Uhr

  • wenn kein Fehler aufgetreten ist, free(f_data); aufgerufen werden soll. thks für den Hinweis

    – Joe Cool

    20. September 2020 um 0:23 Uhr

  • Du hast “auch” falsch geschrieben FILE_TO_LARGE

    – Benutzer11171

    2. September 2021 um 14:31 Uhr

selwyns Benutzeravatar
Selwyn

Wenn es sich bei der Datei um eine Textdatei handelt und Sie den Text Zeile für Zeile abrufen möchten, ist der einfachste Weg die Verwendung von fgets().

char buffer[100];
FILE *fp = fopen("filename", "r");                 // do not use "rb"
while (fgets(buffer, sizeof(buffer), fp)) {
... do something
}
fclose(fp);

1423430cookie-checkWie liest man den Inhalt einer Datei in eine Zeichenfolge in C?

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

Privacy policy