C leggere il file riga per riga

Ho scritto questa funzione per leggere una riga da un file:

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;
}

La funzione legge il file correttamente, e usando la printf vedo che il constLine stringa fatto ottenere a leggere correttamente.

Tuttavia, se uso la funzione ad esempio come questo:

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

printf uscite senza senso. Perché?

  • Utilizzare fgets invece di fgetc. La lettura carattere per carattere, invece di una riga alla volta.
  • Nota che getline() è una parte di POSIX, 2008. Ci possono essere POSIX-like senza di esso, soprattutto se essi non supportano il resto del POSIX 2008, ma nel mondo dei sistemi POSIX, getline() è abbastanza portatile in questi giorni.
InformationsquelleAutor lron | 2010-08-17



15 Replies
  1. 265

    Se il vostro compito non è quello di inventare il, linea per linea, funzione di lettura, ma solo per leggere il file riga per riga, è possibile utilizzare un tipico frammento di codice che coinvolgono il getline() funzione (vedere la pagina di manuale qui):

    #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);
    }
    • Che non è portatile.
    • Più precisamente, questa getline è specifico per GNU libc, cioè, per Linux. Tuttavia, se l’intento è quello di avere una line-funzione di lettura (invece di imparare C), ci sono diversi di pubblico dominio linea-funzioni di lettura disponibili sul web.
    • anche io penso che vuoi free(line) prima della fine di questo ciclo while
    • Perché dovrei farlo? Leggere il manuale, il buffer è riassegnati ad ogni chiamata, quindi dovrebbe essere liberato alla fine.
    • Il if(line) di controllo è superfluo. Chiamata free(NULL) è essenzialmente un no-op.
    • Sono d’accordo, ma questo è un estratto della man pagina. “Prevenire è meglio che curare”, hanno detto. ))
    • Devo dire che questo è piuttosto liscia e la direzione sarà verso… Edit: @mbaitoff Grazie!
    • C’è ancora una perdita di memoria, come si è dimenticato di chiudere il file.
    • Non c’è nessuna tale cosa come la perdita di memoria di scioglimento di un processo.
    • si prega di mostrare a me, in C standard in cui è specificato che le risorse sono liberata automaticamente alla chiusura del programma. Non si può essere sicuri che il sistema operativo farà per voi.
    • Io non sono uno specialista di ricerca di messaggi divini nei libri sacri. Il fatto è che nella vita reale le risorse vengono liberati. Penso che a breve codice di estratti di fornire i concetti e non hanno bisogno di incorporare tutti gli oneri di inizializzazione/deinizializzazione.
    • gratis(NULL) non è definita l’operazione di…
    • Quindi chi ha scritto l’spec fatto una stupida/incauto cosa. Anche se uomo, dice che “se il ptr è NULL, non viene eseguita alcuna operazione”. Che è una buona definizione per me.
    • Se si vuole davvero combattere free(NULL) non specificato (anche se sono abbastanza sicuro che non è per nulla scritti come questo), allora si dovrebbe sapere che anche ls chiamate free(NULL). Dopo il controllo, la pagina man dice che free(ptr); free(ptr); è definito, e che free(NULL) non fa nulla. @mbaitoff Allora perché preoccuparsi di liberare line poi ? Ancora, questo sito è tutto insegnare o aiutare con la migliore soluzione possibile, e la liberazione di ogni memoria allocata che non è più utilizzato è in realtà la buona pratica di avere.
    • Su questo non ci piove!
    • Per chi diceva che questo getline è specifico per GNU libc, “Sia getline() e getdelim() sono stati originariamente estensioni GNU. Essi sono stati standardizzati in POSIX.1-2008.”
    • Francamente, l’allocazione delle risorse e lasciando la responsabilità di deallocazione per l’utente non è davvero un’idea intelligente, di per sé,
    • Per essere completamente onesto questa risposta viene direttamente dalla pagina man. Se una discussione circa la corretta allocazione/dealloc di memoria è per essere avuto, dovrebbe essere posta come una questione a parte e sicuramente è già. L’OP chiaramente solo una domanda sulla funzione a portata di mano e il suo scopo – per leggere un file riga per riga. Quindi a questa domanda non dovrebbe avere nulla a che fare con la corretta gestione della memoria o “buone pratiche”. La sua irrilevante per la domanda originale.
    • non è un operazione definita…”: dal C11 Standard, §7.22.3.3/2, “Se ptr è un puntatore nullo, non viene eseguita alcuna azione.” Il C89 e Standard C99 dire la stessa cosa. Così, free(NULL) è un definito non funzionamento.
    • buona pesca, il mio male.
    • Non ho più accesso alle norme stavo usando quattro anni di convalidare in cui la mia affermazione deriva da, in modo da @DavidBowling può ben essere di destra.
    • il link nel mio commento è per il C11 Progetto di Norma. Ma si può anche leggere questo e questo. A quanto pare, prima di C89, alcune implementazioni è crash su un puntatore null.
    • Interessante. Mi chiedo a quale versione del C standard più giovane di me, è stata la lettura della mia università libreria. Potrebbe essere la pena di una visita alla mia alma mater!

  2. 20

    Nel readLine funzione che restituisca un puntatore a line array (Strettamente parlando, un puntatore al primo carattere, ma la differenza è irrilevante in questo caso). Poiché si tratta di una variabile automatica (cioè, è “in pila”), la memoria è recuperata quando la funzione restituisce. Si vede incomprensibile perchè printf ha messo le sue proprie cose in pila.

    Per la restituzione di un buffer allocato dinamicamente dalla funzione. Hai già uno, è lineBuffer; tutto quello che dovete fare è troncare alla lunghezza desiderata.

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

    AGGIUNTO (risposta alla domanda in commento): readLine restituisce un puntatore a caratteri che compongono la linea. Questo puntatore è ciò che è necessario lavorare con il contenuto di una riga. È anche quello che si deve passare per free quando hai finito di utilizzare la memoria di questi personaggi. Ecco come si potrebbe utilizzare il readLine funzione:

    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). */
    • Come posso free() memoria dopo che mi sono fatto con esso?
    • Ho aggiunto qualcosa alla mia risposta, ma io non sono sicuro di quello che la tua difficoltà è così potrebbe essere off marchio.
    • la risposta è che non è free. Il documento (nella documentazione delle API) il fatto che il buffer restituito è malloc piacerebbe ansd ha bisogno di essere liberato dal chiamante. Quindi le persone che utilizzano la funzione readLine (si spera!) scrivere un codice simile al frammento che Gilles ha aggiunto alla sua risposta.
  3. 18
    FILE* fp;
    char buffer[255];
    
    fp = fopen("file.txt", "r");
    
    while(fgets(buffer, 255, (FILE*) fp)) {
        printf("%s\n", buffer);
    }
    
    fclose(fp);
    • Per me questo comporta la sovrascrittura di ogni riga successiva. Vedere question in base al precedente risposta.
    • Perché il cast (FILE*) fp ? Non è fp è già un FILE * e anche fopen() restituisce un FILE * ?
  4. 13
    //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
    • Ci sono alcuni problemi con questo codice: fopen_s rende il codice unportable. printf cercherà di specificatori di formato e non stampa segni di percentuale e il seguente carattere(s) sono. Null byte di rendere tutti i personaggi del resto della linea vanish. (Non mi dire null byte non può accadere!)
    • E a proposito, non si risolve il problema. L’OP viene descritto che il valore di ritorno della sua funzione scompare. Non vedo intendi affrontare questo problema.
    • Perché non si deve free(line) ogni volta che lo leggi?
    • So che questo è un commento precedente, ma sto aggiungendo questo modo che qualcuno non leggere il suo commento e cercare di connessione(linea) nel ciclo. La memoria per la linea viene assegnato soltanto una volta prima che il ciclo inizia, quindi dovrebbe essere libero soltanto una volta dopo il ciclo termina. Se si tenta di liberare la linea all’interno del ciclo, si ottengono risultati inaspettati. A seconda della modalità di connessione() considera il puntatore. Se si dealloca la memoria e lascia il puntatore che punta alla vecchia posizione, il codice può funzionare. Se si assegna un valore al puntatore quindi la sovrascrittura di una sezione diversa della memoria.
    • printf(linea a) è sbagliato! Non fare questo. Questo apre il codice di una stringa di formato di vulnerabilità in cui si può liberamente leggere/scrivere direttamente alla memoria tramite la roba in corso di stampa. Se dovessi mettere in %n/%p i file e punto il puntatore a un indirizzo in memoria (nella stringa dal file che ho controllato, ho potuto eseguire tale codice.
  5. 9

    readLine() restituisce il puntatore alla variabile locale, che provoca comportamenti indefiniti.

    Per ottenere intorno è possibile:

    1. Creare variabile in funzione chiamante e passa il suo indirizzo readLine()
    2. Allocare memoria per line utilizzando malloc() – in questo caso line sarà persistente
    3. Utilizzare la variabile globale, anche se in genere è una cattiva pratica
  6. 4

    Alcune cose sbagliate con l’esempio:

    • hai dimenticato di aggiungere \n per il tuo printfs. Inoltre, i messaggi di errore dovrebbe andare a stderr cioè fprintf(stderr, ....
    • (non un biggy ma) considerare l’utilizzo di fgetc() piuttosto che getc(). getc() è una macro, fgetc() è una vera e propria funzione
    • getc() restituisce un int così ch deve essere dichiarato come un int. Questo è importante dal momento che il confronto con EOF saranno trattati correttamente. Alcuni set di caratteri a 8 bit utilizzare 0xFF come carattere (ISO-LATIN-1 sarebbe un esempio) e EOF che è -1, sarà 0xFF se assegnato ad un char.
    • C’è un potenziale di buffer overflow nella linea

      lineBuffer[count] = '\0';

      Se la linea è esattamente 128 caratteri, count è di 128, al punto che viene eseguita.

    • Come altri hanno sottolineato, line è un locale dichiarata matrice. Non è possibile restituire un puntatore ad esso.

    • strncpy(count + 1) copia in più count + 1 personaggi, ma terminerà se colpisce '\0' Perché si imposta lineBuffer[count] per '\0' sai che non potrà mai arrivare a count + 1. Tuttavia, se lo facesse, non sarebbe di mettere una terminazione '\0', è necessario farlo. Si capita spesso di vedere qualcosa di simile al seguente:

      char buffer [BUFFER_SIZE];
      strncpy(buffer, sourceString, BUFFER_SIZE - 1);
      buffer[BUFFER_SIZE - 1] = '\0';
    • se si malloc() una linea di ritorno (in luogo del vostro locale char array), il tipo di ritorno dovrebbe essere char* – drop il const.

  7. 3

    Qui è il mio parecchie ore… Leggere tutto il file riga per riga.

    char * readline(FILE *fp, char *buffer)
    {
        int ch;
        int i = 0;
        size_t buff_len = 0;
    
        buffer = malloc(buff_len + 1);
        if (!buffer) return NULL;  //Out of memory
    
        while ((ch = fgetc(fp)) != '\n' && ch != EOF)
        {
            buff_len++;
            void *tmp = realloc(buffer, buff_len + 1);
            if (tmp == NULL)
            {
                free(buffer);
                return NULL; //Out of memory
            }
            buffer = tmp;
    
            buffer[i] = (char) ch;
            i++;
        }
        buffer[i] = '\0';
    
        //Detect end
        if (ch == EOF && (i == 0 || ferror(fp)))
        {
            free(buffer);
            return NULL;
        }
        return buffer;
    }
    
    void lineByline(FILE * file){
    char *s;
    while ((s = readline(file, 0)) != NULL)
    {
        puts(s);
        free(s);
        printf("\n");
    }
    }
    
    int main()
    {
        char *fileName = "input-1.txt";
        FILE* file = fopen(fileName, "r");
        lineByline(file);
        return 0;
    }
  8. 2
    void readLine(FILE* file, char* line, int limit)
    {
        int i;
        int read;
    
        read = fread(line, sizeof(char), limit, file);
        line[read] = '\0';
    
        for(i = 0; i <= read;i++)
        {
            if('\0' == line[i] || '\n' == line[i] || '\r' == line[i])
            {
                line[i] = '\0';
                break;
            }
        }
    
        if(i != read)
        {
            fseek(file, i - read + 1, SEEK_CUR);
        }
    }

    che dire di questo?

  9. 1
    const char *readLine(FILE *file, char* line) {
    
        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);
        return line;
    
    }
    
    
    char linebuffer[256];
    while (!feof(myFile)) {
        const char *line = readLine(myFile, linebuffer);
        printf("%s\n", line);
    }

    di notare che la ‘linea’ variabile è dichiarata in funzione di chiamata e poi passata, in modo da il vostro readLine funzione riempie il buffer predefinito e solo la restituisce. Questo è il modo in cui la maggior parte delle librerie C di lavoro.

    Ci sono altri modi, che io sappia:

    • definire il char line[] come statico
      (static char line[MAX_LINE_LENGTH]
      -> manterrà il suo valore DOPO il ritorno dalla funzione). -> male,
      la funzione non è rientrante, e
      gara condizione può verificarsi -> se si
      chiamare due volte da due thread, si
      sovrascriverà i risultati
    • malloc()ing char linea[], e
      liberando in funzioni di chiamata ->
      troppi costosi mallocs, e,
      delegare la responsabilità di liberare il buffer di un’altra funzione (è la soluzione più elegante è quello di chiamare malloc e free su qualsiasi buffer nella stessa funzione)

    btw, “esplicito” casting da char* per const char* è ridondante.

    btw2, non c’è bisogno di malloc() il lineBuffer, definire solo char lineBuffer[128], quindi non c’è bisogno di liberare

    btw3 non utilizzare il “dynamic dimensioni dello stack di matrici’ (definizione di un array, come char arrayName[some_nonconstant_variable]), se non sapete esattamente cosa state facendo, funziona solo in C99.

    • nota che la ‘linea’ variabile è dichiarata in funzione di chiamata e poi è passato, – probabilmente dovrebbe aver eliminato il locale dichiarazione di linea in funzione. Inoltre, è necessario indicare la funzione per quanto tempo il buffer è che si sta passando, e pensare a una strategia per la gestione di linee che sono troppo lunghi per il buffer si passa.
  10. 1

    Si dovrebbe usare il ANSI funzioni per la lettura di una riga, ad esempio. fgets. Dopo la chiamata è necessario free() per chiamare contesto, ad esempio:

    ...
    const char *entirecontent=readLine(myFile);
    puts(entirecontent);
    free(entirecontent);
    ...
    
    const char *readLine(FILE *file)
    {
      char *lineBuffer=calloc(1,1), line[128];
    
      if ( !file || !lineBuffer )
      {
        fprintf(stderr,"an ErrorNo 1: ...");
        exit(1);
      }
    
      for(; fgets(line,sizeof line,file) ; strcat(lineBuffer,line) )
      {
        if( strchr(line,'\n') ) *strchr(line,'\n')=0;
        lineBuffer=realloc(lineBuffer,strlen(lineBuffer)+strlen(line)+1);
        if( !lineBuffer )
        {
          fprintf(stderr,"an ErrorNo 2: ...");
          exit(2);
        }
      }
      return lineBuffer;
    }
  11. 1

    Implementare il metodo per la lettura, e il contenuto di un file (input1.txt)

    #include <stdio.h>
    #include <stdlib.h>
    
    void testGetFile() {
        //open file
        FILE *fp = fopen("input1.txt", "r");
        size_t len = 255;
        //need malloc memory for line, if not, segmentation fault error will occurred.
        char *line = malloc(sizeof(char) * len);
        //check if file exist (and you can open it) or not
        if (fp == NULL) {
            printf("can open file input1.txt!");
            return;
        }
        while(fgets(line, len, fp) != NULL) {
            printf("%s\n", line);
        }
        free(line);
    }

    Spero che questo aiuto. Codifica felice!

  12. 0

    Si commette l’errore di restituire un puntatore a una variabile automatica.
    La linea della variabile viene allocata nello stack e che vive solo fintanto che la funzione di vita.
    Non è consentito per restituire un puntatore ad esso, perché non appena si restituisce la memoria sarà dato altrove.

    const char* func x(){
        char line[100];
        return (const char*) line; //illegal
    }

    Per evitare questo, si restituiscono un puntatore alla memoria che risiede nel mucchio ad esempio. lineBuffer
    e deve essere l’utente ha la responsabilità di chiamare gratis (quando si è fatto con esso.
    In alternativa, si può chiedere all’utente di passare come argomento un indirizzo di memoria in cui scrivere i contenuti di linea a.

    • C’è una differenza tra illegale e la definizione di comportamento ^^.
    • :p ovviamente non credo che ci sono leggi a riguardo
  13. 0

    Voglio un codice da terra 0 per cui l’ho fatto leggere il contenuto del dizionario della parola, riga per riga.

    char temp_str[20]; //è possibile modificare la dimensione del buffer in base alle vostre esigenze E Una singola linea di lunghezza in un File.

    Nota ho inizializzato il buffer Con il carattere Null ogni volta che ho letto di linea.Questa funzione può essere Automatizzato, Ma perché ho bisogno di Un proof of Concept e desidera progettare un programma di Byte per Byte

    #include<stdio.h>
    
    int main()
    {
    int i;
    char temp_ch;
    FILE *fp=fopen("data.txt","r");
    while(temp_ch!=EOF)
    {
     i=0;
      char temp_str[20]={'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'};
    while(temp_ch!='\n')
    {
      temp_ch=fgetc(fp);
      temp_str[i]=temp_ch;
      i++;
    }
    if(temp_ch=='\n')
    {
    temp_ch=fgetc(fp);
    temp_str[i]=temp_ch;
    }
    printf("%s",temp_str);
    }
    return 0;
    }
    • il programma di lavoro se il vostro staffe erano nel posto giusto 😉 ad esempio int main() {
    • Per inciso, non è necessario specificare tutti i 20 ‘\0’. Si può semplicemente scrivere: code char temp_str[20] = {‘\0’}; code c, viene automaticamente riempire ogni fessura con un carattere di terminazione null quanto il modo in cui le dichiarazioni di matrice lavoro è che se un array viene inizializzato con meno elementi che la matrice contiene, l’ultimo elemento sarà compila i restanti elementi.
    • Credo char temp_str[20] = {0} anche riempie l’intero array di caratteri con terminatori null.
  14. 0

    Mio implementare da zero:

    FILE *pFile = fopen(your_file_path, "r");
    int nbytes = 1024;
    char *line = (char *) malloc(nbytes);
    char *buf = (char *) malloc(nbytes);
    
    size_t bytes_read;
    int linesize = 0;
    while (fgets(buf, nbytes, pFile) != NULL) {
        bytes_read = strlen(buf);
        //if line length larger than size of line buffer
        if (linesize + bytes_read > nbytes) {
            char *tmp = line;
            nbytes += nbytes / 2;
            line = (char *) malloc(nbytes);
            memcpy(line, tmp, linesize);
            free(tmp);
        }
        memcpy(line + linesize, buf, bytes_read);
        linesize += bytes_read;
    
        if (feof(pFile) || buf[bytes_read-1] == '\n') {
            handle_line(line);
            linesize = 0;
            memset(line, '\0', nbytes);
        }
    }
    
    free(buf);
    free(line);

Lascia un commento