2010-11-27 5 views
5

Voilà comment je l'ai fait, mais je ne suis pas sûr que ce soit l'idiome préféré:lecture ligne de fichier c par ligne à l'aide fgetc()

FILE *fp = fopen(argv[0], "r"); 
// handle fopen() returning NULL 

while (!feof(fp)) { 
    char buffer[80]; // statically allocated, may replace this later with some more sophisticated approach 
    int num_chars = 0; 

    for (int ch = fgetc(fp); ch != EOF && ch != '\n'; ch = fgetc()) { 
     buffer[num_chars++] = ch; 
    } 

    // null-terminate the string 
    buffer[num_chars] = '\0'; 

    printf("%s\n", buffer); 
} 

Est-ce d'accord, des suggestions pour améliorer cela?

+1

Il est 'ch = fgetc (fp)'; pas 'ch = fgetc()'. En plus de ça ... une raison pour laquelle vous ne pouvez pas utiliser 'fgets()'? – ThiefMaster

+1

également, vous obtiendrez une erreur de segmentation ici: buffer [num_chars ++] = ch; si une ligne du fichier dépasse 80 caractères. –

+2

while (! Feof (fp)) {, pas nécessaire n'est-ce pas? – Nyan

Répondre

12

Si vous ne comptez pas utiliser fgets() (peut-être parce que vous voulez supprimer le saut de ligne, ou vous voulez traiter avec "\r", "\n" ou "\r\n" fins de ligne, ou si vous voulez savoir combien de caractères ont été lus), vous pouvez utiliser en fonction de squelette:

int get_line(FILE *fp, char *buffer, size_t buflen) 
{ 
    char *end = buffer + buflen - 1; /* Allow space for null terminator */ 
    char *dst = buffer; 
    int c; 
    while ((c = getc(fp)) != EOF && c != '\n' && dst < end) 
     *dst++ = c; 
    *dst = '\0'; 
    return((c == EOF && dst == buffer) ? EOF : dst - buffer); 
} 

Il reconnaît que la nouvelle ligne comme fin de ligne; il laisse tomber la nouvelle ligne. Il ne déborde pas le tampon; il ne rejette pas les caractères en excès, donc s'il est appelé à lire une très longue ligne, il lira la ligne en morceaux; il renvoie le nombre de caractères lus.Si vous avez besoin de faire la distinction entre les trop-plein et une ligne qui se trouve être la longueur de la mémoire tampon - 1, alors vous avez probablement besoin de préserver la nouvelle ligne - avec des changements consécutifs dans le code:

int get_line(FILE *fp, char *buffer, size_t buflen) 
{ 
    char *end = buffer + buflen - 1; /* Allow space for null terminator */ 
    char *dst = buffer; 
    int c; 
    while ((c = getc(fp)) != EOF && dst < end) 
    { 
     if ((*dst++ = c) == '\n') 
      break; 
    } 
    *dst = '\0'; 
    return((c == EOF && dst == buffer) ? EOF : dst - buffer); 
} 

Il existe d'innombrables variantes mineures sur ce point, par exemple en supprimant les caractères en excès si la ligne doit être tronquée. Si vous voulez gérer DOS, (anciens) de fin de ligne Mac ou Unix, puis emprunter une feuille sur le code CSV à partir "The Practice of Programming" par Kernighan & Pike (un excellent livre) et de l'utilisation:

static int endofline(FILE *ifp, int c) 
{ 
    int eol = (c == '\r' || c == '\n'); 
    if (c == '\r') 
    { 
     c = getc(ifp); 
     if (c != '\n' && c != EOF) 
      ungetc(c, ifp); 
    } 
    return(eol); 
} 

vous pouvez utiliser qui en place du test c != '\n':

int get_line(FILE *fp, char *buffer, size_t buflen) 
{ 
    char *end = buffer + buflen - 1; /* Allow space for null terminator */ 
    char *dst = buffer; 
    int c; 
    while ((c = getc(fp)) != EOF && !endofline(fp, c) && dst < end) 
     *dst++ = c; 
    *dst = '\0'; 
    return((c == EOF && dst == buffer) ? EOF : dst - buffer); 
} 

l'autre alternative de traiter l'ensemble du processus est d'utiliser fread() et fwrite():

void copy_file(FILE *in, FILE *out) 
{ 
    char buffer[4096]; 
    size_t nbytes; 
    while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), in)) != 0) 
    { 
     if (fwrite(buffer, sizeof(char), nbytes, out) != nbytes) 
      err_error("Failed to write %zu bytes\n", nbytes); 
    } 
} 

Dans ce contexte, vous souhaitez ouvrir le fichier et de vérifier la validité en, puis appelez:

copy_file(fp, stdout); 
1

Si vous avez besoin de chaque caractère pour l'inspecter ou le modifier ou quoi que ce soit d'autre, utilisez fgets. Pour tout le reste, utilisez fgets.

fgets (buffer, BUFFER_SIZE, fp); 

Notez que fgets lit jusqu'à une nouvelle ligne ou EOF est atteint (ou le tampon est plein bien sûr). Le caractère de nouvelle ligne "\ n" est également ajouté à la chaîne s'il est lu dans le fichier. Le caractère nul est également ajouté.

fgets returns :

En cas de succès, la fonction retourne le même paramètre str.
Si la fin de fichier est rencontrée et qu'aucun caractère n'a été lu, le contenu de str reste inchangé et un pointeur NULL est renvoyé.
Si une erreur se produit, un pointeur NULL est renvoyé.
Utilisez soit ferror soit feof pour vérifier si une erreur s'est produite ou si la fin de fichier a été atteinte.

+0

Votre premier paragraphe semble correspondre à 'use 'fgets()' whatever'. Vouliez-vous dire 'fgetc()' dans la première phrase? –

1

Vous risquez un débordement de tampon si l'utilisateur saisit 80 caractères ou plus.

Je suis avec ThiefMaster: vous devriez plutôt utiliser fgets(). Lisez l'entrée dans un tampon plus grand que toute entrée légitime, puis vérifiez que le dernier caractère est un saut de ligne.

1

À moins que vous ne souhaitiez obtenir un moyen très efficace pour définir le nombre de caractères lus, utilisez fgets().

En remplaçant votre exemple par un fgets() similaire mais différent, vous "perdez" la variable num_chars.

fgets(buffer, sizeof buffer, stdin); 
fputs(buffer, stdout); /* buffer contains a '\n' */ 

Si vous devez supprimer le dernier « \ n »

buffer[0] = 0; 
if (!fgets(buffer, sizeof buffer, stdin)) /* error or eof */; 
num_chars = strlen(buffer); 
if (num_chars && (buffer[num_chars - 1] == '\n')) buffer[--num_chars] = 0; 
puts(buffer); /* add a '\n' to output */ 

Si les chaînes sont vraiment faramineux (comme 42 méga octets d'une valeur), vous pouvez être meilleur caractère lecture de par le caractère et la tenue compter avec num_chars que d'utiliser fgets d'abord et strlen plus tard.

-1

Non und linesize limite strictement C89 (votre code est seulement C99) comme:

FILE *fp = fopen(argv[0], "r"); 
size_t len=1; 
char c, *buffer=calloc(1,1); 
/* handle fopen() returning NULL*/ 
while(c=fgetc(fp),!feof(fp)) 
    if(c=='\n') 
    { 
    puts(buffer); 
    len=1; 
    *buffer=0; 
    } 
    else 
    strncat(buffer=realloc(buffer,++len),&c,1); /* check for NULL needed */ 

puts(buffer); 
free(buffer); 
fclose(fp); 
+2

-1 Pouah! Jamais entendu parler de Schlemiel, le peintre? – pmg

+0

[Doit avoir été sous un rocher] (https://en.wikipedia.org/wiki/Joel_Spolsky#Schlemiel_the_Painter.27s_algorithm). Mais c'est là que je fais la plupart de mon codage. : P –

-2
#include<stdio.h> 
void main() 
{ 
    FILE *fp; 
    char c; 
    int ch=0,w=0,l=0; 
    fp=fopen("c:\read.txt","w"); 
    clrscr(); 
    if(fp==NULL) 
    { 
     printf("\n\n\tDOES NOT EXIXST"); 
     getch(); 
     exit(0); 
    } 
    while(!feof(fp)) 
    { 
     c=fgetc(fp); 

     ch++; 
     if(c==' ') 
     { 
      w++; 
     } 
     if(c=='\n') 
     { 
      l++; 
      w++; 
     } 
    } 

    printf("\n\n\tTOTAL CHAR = %d\n\n\tTOTAL WORDS = %d\n\n\tTOTAL LINES = %d",ch,w,l); 

} 
+3

-4: 'void main()' devrait être int; 'char c = fgetc()' devrait être int; 'clrscr()' et 'getch()' sans prototype; la gestion des espaces blancs dans printf ... ne peut pas vous donner -1 pour chaque avis ... seulement -1 alors – pmg

Questions connexes