C liest Datei Zeile für Zeile

Lesezeit: 8 Minuten

lrons Benutzeravatar
Eisen

Ich habe diese Funktion geschrieben, um eine Zeile aus einer Datei zu lesen:

const char *readLine(FILE *file) {

    if (file == NULL) {
        printf("Error: file pointer is null.");
        exit(1);
    }

    int maximumLineLength = 128;
    char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);

    if (lineBuffer == NULL) {
        printf("Error allocating memory for line buffer.");
        exit(1);
    }

    char ch = getc(file);
    int count = 0;

    while ((ch != '\n') && (ch != EOF)) {
        if (count == maximumLineLength) {
            maximumLineLength += 128;
            lineBuffer = realloc(lineBuffer, maximumLineLength);
            if (lineBuffer == NULL) {
                printf("Error reallocating space for line buffer.");
                exit(1);
            }
        }
        lineBuffer[count] = ch;
        count++;

        ch = getc(file);
    }

    lineBuffer[count] = '\0';
    char line[count + 1];
    strncpy(line, lineBuffer, (count + 1));
    free(lineBuffer);
    const char *constLine = line;
    return constLine;
}

Die Funktion liest die Datei korrekt, und mit printf sehe ich, dass die constLine-Zeichenfolge auch korrekt gelesen wurde.

Wenn ich die Funktion jedoch zB so verwende:

while (!feof(myFile)) {
    const char *line = readLine(myFile);
    printf("%s\n", line);
}

printf gibt Kauderwelsch aus. Wieso den?

  • Verwenden fgets Anstatt von fgetc. Sie lesen Zeichen für Zeichen statt Zeile für Zeile.

    – Scheib

    17. März 2017 um 4:00 Uhr

  • Beachten Sie, dass getline() ist ein Teil von POSIX 2008. Es kann POSIX-ähnliche Plattformen ohne sie geben, insbesondere wenn sie den Rest von POSIX 2008 nicht unterstützen, aber innerhalb der Welt der POSIX-Systeme, getline() ist heutzutage ziemlich tragbar.

    – Jonathan Leffler

    8. Mai 2017 um 21:20 Uhr

  • Linie[count+1] ist eine automatische Stapelvariable und Sie verwenden einen Zeiger darauf als Rückgabewert. Das ist U.B.

    – ulix

    13. September um 3:48

Benutzeravatar von mbaitoff
mbaitoff

Wenn Ihre Aufgabe nicht darin besteht, die zeilenweise Lesefunktion zu erfinden, sondern nur die Datei zeilenweise zu lesen, können Sie ein typisches Code-Snippet mit verwenden getline() Funktion (siehe Handbuchseite hier):

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

int main(void)
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    fp = fopen("/etc/motd", "r");
    if (fp == NULL)
        exit(EXIT_FAILURE);

    while ((read = getline(&line, &len, fp)) != -1) {
        printf("Retrieved line of length %zu:\n", read);
        printf("%s", line);
    }

    fclose(fp);
    if (line)
        free(line);
    exit(EXIT_SUCCESS);
}

  • Genauer gesagt diese getline ist spezifisch für GNU libc, dh für Linux. Wenn jedoch die Absicht besteht, eine Zeilenlesefunktion zu haben (im Gegensatz zum Erlernen von C), gibt es mehrere gemeinfreie Zeilenlesefunktionen, die im Internet verfügbar sind.

    – Gilles ‘SO- hör auf, böse zu sein’

    17. August 2010 um 11:55 Uhr

  • Warum sollte ich das tun? Lesen Sie das Handbuch, Puffer wird bei jedem Aufruf neu zugewiesen, dann sollte er am Ende freigegeben werden.

    – mbaitoff

    30. November 2012 um 12:43 Uhr

  • Das if(line) Prüfung ist überflüssig. Berufung free(NULL) ist im Wesentlichen ein No-Op.

    – aroth

    28. Januar 2014 um 7:25 Uhr

  • @PhilipAdler Wenn du dich wirklich streiten willst free(NULL) nicht spezifiziert ist (obwohl ich mir ziemlich sicher bin, dass es nirgendwo so geschrieben steht), dann sollten Sie das sogar wissen ls Anrufe free(NULL). Nach Überprüfung sagt die Manpage das free(ptr); free(ptr); ist undefiniert, und das free(NULL) tut nichts. @mbaitoff Warum machst du dir dann die Mühe zu befreien line dann ? Dennoch dreht sich auf dieser Website alles um das Unterrichten oder Helfen bei der Beste Lösung möglich, und das Freigeben von jedem zugewiesenen Speicher, der nicht mehr verwendet wird, ist eigentlich die gute Praxis.

    – Jerska

    13. Juni 2014 um 18:59 Uhr


  • Für diejenigen, die sagten, dass diese getline spezifisch für GNU libc ist: „Sowohl getline() als auch getdelim() waren ursprünglich GNU-Erweiterungen. Sie wurden in POSIX.1-2008 standardisiert.“

    – willkill07

    21. April 2015 um 21:01 Uhr

Benutzeravatar von Rob
rauben

FILE* filePointer;
int bufferLength = 255;
char buffer[bufferLength]; /* not ISO 90 compatible */

filePointer = fopen("file.txt", "r");

while(fgets(buffer, bufferLength, filePointer)) {
    printf("%s\n", buffer);
}

fclose(filePointer);

  • Bei mir führt das dazu, dass jede Zeile mit der nächsten überschrieben wird. Siehe diese Frage basierend auf der obigen Antwort.

    – Cézar Cobuz

    8. Januar 2019 um 3:09 Uhr


  • Warum die Besetzung (FILE*) fp ? Ist nicht fp ist schon ein FILE * und auch fopen() gibt a zurück FILE * ?

    – Buchhalter م

    4. April 2019 um 23:25 Uhr


  • Wenn Sie damit einverstanden sind, dass die Zeilen auf eine bestimmte Länge begrenzt sind, ist dies die beste Antwort. Ansonsten verwenden getline ist eine gute Alternative. Ich stimme dem zu FILE * Besetzung ist unnötig.

    – das Feuer

    17. Oktober 2019 um 2:18 Uhr


  • Ich habe den unnötigen Cast entfernt, eine Variable für die Pufferlänge hinzugefügt und geändert fp zu filePointer für mehr Klarheit.

    – Rauben

    6. Januar 2020 um 11:13 Uhr

  • Es sollte const int bufferLength sein, wenn es umständlich ist 🙂

    – baz

    12. November 2021 um 20:34 Uhr


Gilles 'SO-hör auf, böse zu sein' Benutzer-Avatar
Gilles ‘SO- hör auf, böse zu sein’

In deiner readLine Funktion geben Sie einen Zeiger auf die zurück line array (Genau genommen ein Zeiger auf sein erstes Zeichen, aber der Unterschied ist hier irrelevant). Da es sich um eine automatische Variable handelt (dh „auf dem Stack“), wird der Speicher zurückgefordert, wenn die Funktion zurückkehrt. Sie sehen Kauderwelsch, weil printf hat seine eigenen Sachen auf den Stapel gelegt.

Sie müssen einen dynamisch zugewiesenen Puffer von der Funktion zurückgeben. Sie haben bereits eine, es ist lineBuffer; Alles, was Sie tun müssen, ist es auf die gewünschte Länge zu kürzen.

    lineBuffer[count] = '\0';
    realloc(lineBuffer, count + 1);
    return lineBuffer;
}

HINZUGEFÜGT (Antwort auf die Folgefrage im Kommentar): readLine gibt einen Zeiger auf die Zeichen zurück, aus denen die Zeile besteht. Diesen Zeiger benötigen Sie, um mit dem Inhalt der Zeile zu arbeiten. Es ist auch das, wozu Sie übergehen müssen free wenn Sie den von diesen Charakteren belegten Speicher verbraucht haben. So können Sie die verwenden readLine Funktion:

char *line = readLine(file);
printf("LOG: read a line: %s\n", line);
if (strchr(line, 'a')) { puts("The line contains an a"); }
/* etc. */
free(line);
/* After this point, the memory allocated for the line has been reclaimed.
   You can't use the value of `line` again (though you can assign a new value
   to the `line` variable if you want). */

  • @Iron: Ich habe meiner Antwort etwas hinzugefügt, aber ich bin mir nicht sicher, was Ihre Schwierigkeit ist, also könnte es daneben liegen.

    – Gilles ‘SO- hör auf, böse zu sein’

    17. August 2010 um 11:53 Uhr

  • @Iron: Die Antwort ist, dass Sie es nicht befreien. Sie dokumentieren (in der API-Dokumentation) die Tatsache, dass der zurückgegebene Puffer malloc’d ist und vom Aufrufer freigegeben werden muss. Dann werden Leute, die Ihre readLine-Funktion verwenden, (hoffentlich!) Code schreiben, der dem Snippet ähnelt, das Gilles seiner Antwort hinzugefügt hat.

    – JeremyP

    17. August 2010 um 12:27 Uhr

Benutzeravatar von RevoLab
RevoLab

//open and get the file handle
FILE* fh;
fopen_s(&fh, filename, "r");

//check if file exists
if (fh == NULL){
    printf("file does not exists %s", filename);
    return 0;
}


//read line by line
const size_t line_size = 300;
char* line = malloc(line_size);
while (fgets(line, line_size, fh) != NULL)  {
    printf(line);
}
free(line);    // dont forget to free heap memory

Benutzeravatar von gsamaras
Gsamaras

Vollständig, fgets() Lösung:

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

#define MAX_LEN 256

int main(void)
{
    FILE* fp;
    fp = fopen("file.txt", "r");
    if (fp == NULL) {
      perror("Failed: ");
      return 1;
    }

    char buffer[MAX_LEN];
    while (fgets(buffer, MAX_LEN, fp))
    {
        // Remove trailing newline
        buffer[strcspn(buffer, "\n")] = 0;
        printf("%s\n", buffer);
    }

    fclose(fp);
    return 0;
}

Ausgabe:

First line of file
Second line of file
Third (and also last) line of file

Denken Sie daran, wenn Sie aus der Standardeingabe lesen möchten (und nicht wie in diesem Fall aus einer Datei), müssen Sie nur übergeben stdin als dritter Parameter von fgets() Methode, wie folgt:

while(fgets(buffer, MAX_LEN, stdin))

Anhang

Entfernen des nachgestellten Newline-Zeichens aus der fgets()-Eingabe

wie man erkennt, dass eine Datei geöffnet ist oder nicht in c

  • Hallo, @gsamaras Ich denke, wir können direkt passieren MAX_LEN zu fgets. Diese Beschreibung habe ich gefunden in: linux.die.net/man/3/fgets “` “`

    Benutzer10277898

    17. November 2020 um 10:23 Uhr


  • Hey @juancortez, ich gehe vorbei MAX_LEN - 1 beim 2. Argument der Methode in der Tat!

    – Gsamaras

    17. November 2020 um 19:36 Uhr

  • Es besteht keine Notwendigkeit für die -1 in MAX_LEN - 1fgets(buffer, n, fp) liest bereits bis zu n-1 Zeichen und reserviert Platz für die Nullterminierung.

    – マルちゃん だよ

    26. November 2021 um 13:46 Uhr

  • @マルちゃん だよ Ja, du hast Recht cplusplus.com/reference/cstdio/fgetsAntwort aktualisiert.

    – Gsamaras

    28. November 2021 um 8:49 Uhr

readLine() gibt Zeiger auf lokale Variable zurück, was zu undefiniertem Verhalten führt.

Um herumzukommen, können Sie:

  1. Erstellen Sie eine Variable in der Aufruferfunktion und übergeben Sie ihre Adresse an readLine()
  2. Speicher zuweisen für line verwenden malloc() – in diesem Fall line wird hartnäckig sein
  3. Verwenden Sie globale Variablen, obwohl dies im Allgemeinen eine schlechte Praxis ist

  • Hallo, @gsamaras Ich denke, wir können direkt passieren MAX_LEN zu fgets. Diese Beschreibung habe ich gefunden in: linux.die.net/man/3/fgets “` “`

    Benutzer10277898

    17. November 2020 um 10:23 Uhr


  • Hey @juancortez, ich gehe vorbei MAX_LEN - 1 beim 2. Argument der Methode in der Tat!

    – Gsamaras

    17. November 2020 um 19:36 Uhr

  • Es besteht keine Notwendigkeit für die -1 in MAX_LEN - 1fgets(buffer, n, fp) liest bereits bis zu n-1 Zeichen und reserviert Platz für die Nullterminierung.

    – マルちゃん だよ

    26. November 2021 um 13:46 Uhr

  • @マルちゃん だよ Ja, du hast recht cplusplus.com/reference/cstdio/fgetsAntwort aktualisiert.

    – Gsamaras

    28. November 2021 um 8:49 Uhr

Benutzeravatar von Raku Escape
Raku-Flucht

Verwenden fgets() um eine Zeile aus einem Dateihandle zu lesen.

1426320cookie-checkC liest Datei Zeile für Zeile

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

Privacy policy