web-dev-qa-db-fra.com

C lire le fichier ligne par ligne

J'ai écrit cette fonction pour lire une ligne d'un fichier:

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 fonction lit le fichier correctement et, en utilisant printf, je constate que la chaîne constLine a également été lue correctement.

Cependant, si j’utilise la fonction, par exemple comme ça:

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

printf produit du charabia. Pourquoi?

142
lron

Si votre tâche n'est pas d'inventer la fonction de lecture ligne par ligne, mais simplement de lire le fichier ligne par ligne, vous pouvez utiliser un extrait de code typique impliquant la fonction getline() (voir la page de manuel ici ) :

#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);
}
246
mbaitoff

Dans votre fonction readLine, vous renvoyez un pointeur sur le tableau line (à proprement parler, un pointeur sur son premier caractère, mais la différence n'est pas pertinente ici). Comme il s’agit d’une variable automatique (c’est-à-dire «sur la pile»), la mémoire est récupérée au retour de la fonction. Vous voyez charabia parce que printf a mis ses propres données dans la pile.

Vous devez renvoyer une mémoire tampon allouée dynamiquement à partir de la fonction. Vous en avez déjà un, c'est lineBuffer; tout ce que vous avez à faire est de le tronquer à la longueur souhaitée.

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

AJOUT&EACUTE;(réponse à la question suivante dans le commentaire): readLine renvoie un pointeur sur les caractères constituant la ligne. Ce pointeur est ce dont vous avez besoin pour travailler avec le contenu de la ligne. C'est aussi ce que vous devez passer à free lorsque vous avez fini d'utiliser la mémoire occupée par ces personnages. Voici comment utiliser la fonction readLine:

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). */
19
Gilles
FILE* fp;
char buffer[255];

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

while(fgets(buffer, 255, (FILE*) fp)) {
    printf("%s\n", buffer);
}

fclose(fp);
16
Rob
//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
14
RevoLab

readLine() renvoie le pointeur sur la variable locale, ce qui entraîne un comportement indéfini.

Pour vous déplacer, vous pouvez:

  1. Créer une variable dans la fonction appelant et transmettre son adresse à readLine()
  2. Allouez de la mémoire pour line en utilisant malloc() - dans ce cas, line sera persistant
  3. Utiliser la variable globale, bien que ce soit généralement une mauvaise pratique
8
qrdl

Utilisez fgets() pour lire une ligne depuis un descripteur de fichier.

5
Raku Escape

Certaines choses ne vont pas dans l'exemple:

  • vous avez oublié d'ajouter\n à vos printfs. Les messages d'erreur doivent également aller à stderr i.e. fprintf(stderr, ....
  • (pas une grosse mais mais) pensez à utiliser fgetc() plutôt que getc(). getc() est une macro, fgetc() est une fonction appropriée
  • getc() renvoie int alors ch doit être déclaré comme int. Ceci est important car la comparaison avec EOF sera gérée correctement. Certains jeux de caractères 8 bits utilisent 0xFF comme caractère valide (par exemple, ISO-LATIN-1) et EOF qui vaut -1, sera 0xFF s'il est affecté à une char.
  • Il y a un débordement de tampon potentiel sur la ligne

    lineBuffer[count] = '\0';
    

    Si la ligne comporte exactement 128 caractères, count vaut 128 au point qui est exécuté.

  • Comme d'autres l'ont fait remarquer, line est un tableau déclaré localement. Vous ne pouvez pas y retourner un pointeur.

  • strncpy(count + 1) copiera au maximum count + 1 caractères mais s’achèvera s’il détecte '\0'. Parce que vous définissez lineBuffer[count] sur '\0', vous savez qu’il n’obtiendra jamais count + 1. Cependant, si tel était le cas, il ne mettrait pas de '\0' de terminaison; Vous voyez souvent quelque chose comme ce qui suit:

    char buffer [BUFFER_SIZE];
    strncpy(buffer, sourceString, BUFFER_SIZE - 1);
    buffer[BUFFER_SIZE - 1] = '\0';
    
  • si vous malloc() une ligne à renvoyer (à la place de votre tableau char local), votre type de retour doit être char* - supprimez la const.

4
JeremyP
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);
    }
}

qu'en est-il de celui-ci?

2
Taner Mansur

Voici mes quelques heures ... Lire tout le fichier ligne par ligne.

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;
}
2
Sam
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);
}

notez que la variable 'line' est déclarée dans la fonction appelante puis transmise, donc votre fonction readLine remplit la mémoire tampon prédéfinie et la renvoie simplement. C'est ainsi que fonctionnent la plupart des bibliothèques C.

À ma connaissance, il y a d'autres moyens: 

  • définir le char line[] comme statique (static char line[MAX_LINE_LENGTH]-> il conservera sa valeur APRES le retour de la fonction). -> mauvais, la fonction n'est pas réentrante, et une condition de concurrence peut se produire -> si vousappelez-la deux fois à partir de deux threads, elle remplacera ses résultats
  • malloc()ing la ligne de caractère [], et le libérant dans les fonctions d'appel -> trop coûteux mallocs, et, déléguant la responsabilité de libérer le tampon à une autre fonction (la solution la plus élégante consiste à appeler malloc et free sur les tampons de la même fonction)

btw, la diffusion 'explicite' de char* à const char* est redondante.

btw2, il n'y a pas besoin de malloc() le lineBuffer, il suffit de le définir char lineBuffer[128], de sorte que vous n'avez pas besoin de le libérer

btw3 n'utilise pas de 'tableaux de piles de taille dynamique' (définissant le tableau comme étant char arrayName[some_nonconstant_variable]), si vous ne savez pas exactement ce que vous faites, cela ne fonctionne que dans C99.

1
nothrow

Vous devez utiliser les fonctions ANSI pour lire une ligne, par exemple. Fgets. Après avoir appelé, vous avez besoin de free () dans un contexte d’appel, par exemple:

...
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;
}
1
user411313

Implémenter une méthode pour lire et obtenir le contenu d'un fichier (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);
}

J'espère que cette aide. Bonne codage!

1
Nhat Dinh

Fournissez une fonction portable et générique _read_line_ et vous pourrez traiter le contenu de la ligne ligne par ligne.

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

void
read_line(const char *filename,
          size_t linecap,
          int delimiter,
          void (*process_line)(const char *line, ssize_t linelen)) {
    FILE *file = fopen(filename, "r");
    if (!file) {
        perror(filename);
        return;
    }
    int c;
    char *line = malloc(linecap);
    if (0 == line) {
        perror("linecap");
        goto close_exit;
    }
    char *p = line;
    ssize_t linelen;
    while (EOF != (c = fgetc(file))) {
        if (delimiter == c) {
            *p = delimiter, *++p = 0;
            process_line(line, linelen+1);
            p = line;
            linelen = 0;
        } else {
            *p++ = c;
            linelen++;
        }
    }
    free(line);
    if (ferror(file)) {
        perror(filename);
        goto close_exit;
    }
 close_exit:
    fclose(file);
}

void
print_line(const char *line, ssize_t linelen) {
    fwrite(line, 1, linelen, stdout);
    fflush(stdout);
}

int
main(int argc, char **argv) {
  read_line("/a/b/c/some.txt", 16, '\n', print_line);
  return 0;
}
_
0
南山竹

Mon outil à partir de zéro:

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);
0
tjeubaoit

Vous commettez l’erreur de renvoyer un pointeur sur une variable automatique . La ligne de variable est allouée dans la pile et ne vit que tant que la fonction est en vie . Vous n’avez pas le droit de lui retourner un pointeur, dès qu'il reviendra, la mémoire sera donnée ailleurs.

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

Pour éviter cela, vous devez soit retourner un pointeur à la mémoire qui réside sur le tas, par exemple. lineBuffer .__ et il devrait incomber à l'utilisateur d'appeler free () lorsqu'il en aura terminé . Sinon, vous pouvez demander à l'utilisateur de vous transmettre en argument une adresse mémoire sur laquelle écrire le contenu de la ligne.

0
Lefteris E

Je veux un code de la terre 0 alors j'ai fait ceci pour lire le contenu du mot du dictionnaire ligne par ligne.

char temp_str [20]; // vous pouvez modifier la taille de la mémoire tampon selon vos besoins et la longueur d'une seule ligne dans un fichier.

Note J'ai initialisé le tampon avec un caractère nul chaque fois que je lis une ligne. 

#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;
}
0
Mohit Dabas