Odczytywanie plików w języku C¶
Informacja
Artykuł ten pochodzi ze starej wersji tego bloga (rok 2006) i ma na celu pokazanie, jak poprawnie odczytywać pliki dowolnego typu w języku C/C++.
Co ciekawe testowałem jego działanie całkiem niedawno i w obu przypadkach działa to poprawnie - jednakże pamiętam, że miałem pewien problem przy czytaniu danych - pewnie były to dane binarne.
Podpowiedź
Różnice w działaniu są widoczne przy odczytywaniu pliku który ma wielkość 0 bajtów.
Wstęp¶
Gdy przychodzi do odczytywania plików programista myśli najczęściej coś w stylu „dopóki nie koniec pliku, czytaj i przetwarzaj dane”, co kończy się w taki sposób.
Przykład niepoprawny!!!¶
#include <stdio.h>
#include <stdlib.h>
#define MYFILE "test.txt"
int main(int argc, char **argv) {
FILE *fp;
char buf[BUFSIZ];
int i;
if ((fp = fopen(MYFILE, "r")) == NULL) {
perror (MYFILE);
return (EXIT_FAILURE);
}
i = 0;
while (!feof(fp)){
fgets(buf, BUFSIZ, fp);
printf ("Line %4d: %s", i, buf);
i++;
}
printf("\n");
fclose(fp);
return(EXIT_SUCCESS);
}
Z tego co się orientuje taka konstrukcja jest zupełnie poprawna w językach takich jak
Pascal
PHP
Jednakże w języku C ten kawałek kodu zawiera poważny błąd, ponieważ
funkcja feof()
służy do sprawdzenia czy koniec koniec pliku został już
osiągnięty, powoduje to „przeczytanie” podwójnie ostatniej linii z pliku
wejściowego.
Jak można przeczytać w manualu:
The feof function
Synopsis
1 #include <stdio.h>
int feof(FILE *stream);
Description
2 The feof function tests the end-of-file indicator for the stream pointed to by stream.
Returns
3 The feof function returns nonzero if and only if the end-of-file indicator is set for stream.
Funkcja feof()
testuje strumień ma ustawiony znacznik oznaczający koniec pliku, a nie czy nastąpił sam koniec pliku.
Oznacza to że taki identyfikator jest ustawiany przez inną funkcję - funkcję która odczytuje dane.
Można przyjąć, że tra funkcja czyta wszystkie dane, ale w momencie napotkania końca pliku ustawia znacznik EOF na strumieniu.
Wersja poprawna¶
#include <stdio.h>
#include <stdlib.h>
#define MYFILE "test.txt"
int main(int argc, char **argv) {
FILE *fp;
char buf[BUFSIZ];
int i;
if ((fp = fopen(MYFILE, "r")) == NULL) {
perror (MYFILE);
return (EXIT_FAILURE);
}
i = 0;
while (fgets(buf, BUFSIZ, fp) != NULL) {
printf ("Line %4d: %s", i, buf);
i++;
}
printf("\n");
if (feof(fp)) {
printf("EOF Reached\n");
}
fclose(fp);
return(EXIT_SUCCESS);
}
By uniknąć tej przykrej przypadłości należy czytać pliki tak jak wyżej.
Dzięki temu, zawsze jest sprawdzany wynik działania funkcji read()
- pozwala to ustawić znaczink EOF
na strumieniu.
Inne przykłady poprawego czytania wejścia¶
int total = 0;
while (fscanf(fp, "%d", &num) == 1) {
total += num;
}
printf ("Total is %d\n", total);
int c;
while ((c = fgetc(fp)) != EOF) {
putchar (c);
}
To na tyle ;).