2010-06-21 7 views
4

En C, existe-t-il un moyen de lire un fichier texte ligne par ligne sans connaître l'espace à allouer?Lecture d'un fichier avec des longueurs de ligne variables ligne par ligne c

est ici un exemple de ce que je veux dire:

fgets(line, <dynamic line size>, fileHandle); 

Merci pour l'aide!

+0

Quelle est la taille du fichier? Si le fichier n'est pas trop grand, vous pouvez juste allouer de la mémoire égale à la taille du fichier et y lire simplement des choses. –

Répondre

5

Rien d'automatique. Vous devez continuer à augmenter votre tampon et à appeler les fgets jusqu'à ce que vous obteniez le retour à la ligne ou l'EOF.

// NOTE: not production ready as does not handle memory allocation failures 
size_t alloced = 128; 
char *p = malloc(alloced); 
char *walk = p; 
size_t to_read = alloced; 

for (;;) { 
    if (fgets(walk, to_read, fp) == NULL) 
     break; 

    if (walk[strlen(walk) - 1] == '\n') 
     break; 

    to_read = alloced; 
    alloced *= 2; 

    p = realloc(p, allocated); 
    walk = p + to_read; 
} 
+0

Cette opération échoue sur les lignes avec NUL incorporés. –

+1

Le texte ne dispose pas de nuls intégrés – nos

+0

Comment ça, @nos? Oui, les chaînes C utilisent des terminaisons NUL. Cependant, NUL est un caractère Unicode et ASCII valide, et les codages de celui-ci contiennent généralement des octets NUL incorporés. –

0

Pas directement.

Pour résoudre ce problème, vous devrez vous préparer à gérer fgets si le tampon n'est pas assez grand. Commencez par malloc en ajoutant line à un tampon initial raisonnable (256 caractères, par exemple), puis realloc à deux fois cette taille à chaque fois que fgets renvoie NULL.

2

Si vous avez glibc ou d'une autre libc qui prend en charge POSIX (2008), vous pouvez utiliser getline:

ssize_t getline(char **lineptr, size_t *n, FILE *stream); 

getline() lit une ligne complète de flux , stocker l'adresse du tampon contenant le texte en * lineptr. Le tampon est terminé par un caractère nul et inclut le caractère de retour à la ligne , le cas échéant.

Si * lineptr est NULL, getline() allouera un tampon pour stocker la ligne, qui devrait être libéré par le programme utilisateur . (La valeur en * n est ignorée.)

+1

Ceci est [POSIX standard] (http://www.opengroup.org/onlinepubs/9699919799/functions/getline.html), et il est pris en charge par ex. [FreeBSD] (http://www.unix.com/man-page/FreeBSD/3/getline/) et [NetBSD] (http://netbsd.gw.com/cgi-bin/man-cgi?getline+ 3 + NetBSD-current). –

0

Pour votre 'taille de ligne dynamique', utilisez simplement le maximum de mémoire que vous souhaitez utiliser. Si la ligne n'est pas complète, traitez la pièce que vous avez utilisée et faites quelques opérations supplémentaires jusqu'à la fin de la ligne. Utilisez strlen pour vous aider à déterminer si vous avez lu une ligne entière.

void ProcessFile(FILE *fp) 
{ 
    int len = 0; 
    char lineBuf[ MAX_SIZE ]; 

    while(!feof(fp)) 
    { 
     do 
     { 
      if(fgets(lineBuf, MAX_SIZE, fp) > 0) 
      { 
       fputs(lineBuf, STDOUT); 
       len = strlen(lineBuf); 
      } 
     } while(!feof(fp) && lineBuf[len-1] != '\n'); 

     puts("A line has been processed!"); 
    } 

    return; 
} 
1

Fondamentalement, vous devez allouer un tampon temporaire de taille arbitraire. Ensuite, vous devez analyser l'entrée pour le caractère de nouvelle ligne, remplissant le tampon avec les caractères numérisés. Si le tampon se remplit, allouer un nouveau tampon plus grand, copier l'ancien contenu dans le nouveau tampon et libérer l'ancien tampon.

La bibliothèque Glib a la fonction g_io_channel_read_line qui fait cela pour vous.

0
char *myGetLine(FILE *pFile) 
{ 
    //Allocation a chunk of memory. 
    //Read a chunk from the file. 
    //While not a full line then reallocate a bigger chunk of memory and get the next chunk from the file. 
    //NOTE: No malloc()/realloc() error checking is done here. 
    //NOTE: Each call allocates a chunk of memory that the user must free(). 

    const int bufIncrSize = 128; //or whatever increment you like 
    int bufSize = bufIncrSize; 
    char *pLine = (char *)malloc(bufIncrSize); 
    pLine[0] = '\0'; //make it an empty string 

    //while not EOF 
    while (fgets(&pLine[strlen(pLine)], bufIncrSize, pFile) != NULL) { 
    // If we got the newline, then we have the whole line 
    if (pLine[strlen(pLine) - 1] == '\n') 
     break; 

    //else get a bigger buffer and try again 
    bufSize += bufIncrSize; 
    pLine = (char *)realloc(pLine, bufSize); 
    } 

    return pLine; //NOTE the user is responsible for freeing the line buffer 
} 
0

Vous liriez un morceau de la ligne à la fois dans un tampon de taille fixe, puis copiez le contenu de ce tampon de taille fixe dans une mémoire tampon allouée dynamiquement et redimensionnable:

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

#define SIZE ... // some reasonable size to handle most cases 

int getNextLine(FILE *stream, char **line, size_t *lineLength) 
{ 
    char inbuf[SIZE]; 
    int done = 0; 
    int rval = 1; // success 

    *lineLength = 0; 

    /** 
    * If *line is not NULL, it is assumed that it was allocated on a 
    * previous call to getNextLine. Free it and set to NULL. 
    */ 
    if (*line != NULL) 
    { 
    free(*line); 
    *line = NULL; 
    } 

    while(!done) 
    { 
    char *tmp; 

    if (fgets(inbuf, sizeof inbuf, stream)) 
    { 
     /** 
     * Check for newline character. If present, clear it and set the 
     * done flag to true. 
     */ 
     char *newline = strchr(inbuf, '\n'); 
     if (newline != NULL) 
     { 
     *newline = 0; 
     done = 1; 
     } 

     /** 
     * Extend the dynamic buffer by the length of the input string 
     * and copy the input string to it. 
     */ 
     tmp = realloc(*line, *lineLength + strlen(inbuf) + 1); 
     if (tmp) 
     { 
     *line = tmp; 
     (*line)[*lineLength] = 0;  
     strcat(*line, inbuf);   
     *lineLength += strlen(inbuf) + 1; 
     } 
     else 
     { 
     printf("Error allocating or extending buffer\n"); 
     rval = 0; 
     done = 1; 
     } 
    } 
    else 
    { 
     if (feof(stream)) 
     { 
     printf("At end-of-file\n"); 
     rval = EOF; 
     } 
     else 
     { 
     printf("Error during read\n"); 
     rval = 0; 
     } 
     done = 1; 
    } 
    } 
    return rval; 
} 

int main(void) 
{ 
    char *line = NULL;  // line *MUST* be initialized to NULL 
    size_t lineLength = 0; 
    int status; 

    for (;;) 
    { 
    int status = getNextLine(stdin, &line, &lineLength); 
    if (status == 0 || status == EOF) 
     break; 

    printf("Read %lu characters in line: \"%s\"\n", 
     (unsigned long) lineLength, line); 
    } 
    return 0; 
} 
Questions connexes