Pliki i funkcje wejścia/wyjścia
Otwieranie i zamykanie pliku
Czytanie i zapisywanie do pliku
wymaga jego uprzedniego otwarcia.
Plik możemy otworzyć funkcją
fopen, której musimy podać dwa argumenty:
- ścieżkę do pliku w postaci napisu
np.
"plik.txt","/etc/fstab","C:\CONFIG.SYS", - tryb otwarcia pliku — również napis:
"r"— tylko do odczytu, znacznik na początku pliku,"r+"— do odczytu i zapisu, znacznik na początku pliku,"w"— tylko do zapisu, czyszczenie pliku (jeśli istniał), znacznik na początku pliku,"w+"— do odczytu i zapisu, czyszczenie pliku (jeśli istniał), znacznik na początku pliku,"a"— (tryb dopisywania — "appending") tylko do zapisu, znacznik na końcu pliku,"a+"— do odczytu i zapisu, znacznik na końcu pliku.
Funkcja fopen zwraca uchwyt do otwartego pliku,
jeśli czynność się powiodła. W przeciwnym razie zwracana jest wartość NULL.
FILE * f; f = fopen("program.c", "r"); if(!f){ perror("fopen"); exit(1); }
Funkcja perror wypisuje przyczynę wystąpienia błędu na standardowe wyjście błędów.
Gdy już zakończymy wszystkie działania na pliku,
należy go koniecznie zamknąć za pomocą funkcji fclose.
fclose(f);
Standardowe wejście, wyjście i wyjście błędów
Najczęściej w programie w momencie uruchamiania
dostępne są trzy otwarte pliki:
stdin, stdout, stderr.
Pierwszy do odczytu, dwa pozostałe do zapisu.
Można ich używać jak zwykłych plików, przy czym najczęściej są powiązane z wejściem z klawiatury oraz wyjściem na ekran.
Czytanie z pliku
Mając otwarty do odczytu plik, możemy z niego czytać dane.
Pamiętaj, że odczyt informacji z dysku jest wielokrotnie wolniejszy, niż odczyt z pamięci operacyjnej. Dlatego w miarę możliwości staraj się unikać np. dwukrotnego wczytywania zawartości pliku, jeśli możliwe jest jednokrotne wczytanie całego pliku do pamięci operacyjnej.
Funkcja fscanf działa analogicznie do funkcji scanf, przy czym różnica
polega na tym, że jawnie podajemy, z jakiego pliku ma się odbywać czytanie.
int zmienna, wczytano; wczytano = fscanf(f, "%d", &zmienna); if(!wczytano){ fprintf(stderr, "W pliku nie znaleziono liczby.\n"); } else if(wczytano == EOF) { fprintf(stderr, "Osiągnięto koniec pliku.\n"); } else { printf("Wczytana z pliku liczba to %d.", zmienna); }
Znanej nam już funkcji fgets używamy do wczytywania całych linii tekstu.
char napis[100]; fgets(napis, 100, f); printf("Wczytano linię: '%s'", napis);
Do wczytywania pojedynczych znaków
możemy użyć funkcji fgetc.
int znak; znak = fgetc(f); if(znak == EOF){ fprintf(stderr, "Został osiągnięty koniec pliku.\n"); } else { printf("Wczytano znak '%c'.\n", znak); }
Warto zwrócić uwagę, że zmienna znak w powyższym
przykładzie jest typu int.
Tylko wtedy porównywanie jej wartości do EOF ma sens,
ponieważ wartość EOF jest spoza zakresu typu char.
Zapisywanie do pliku
Analogicznie do funkcji printf
działa funkcja fprintf.
fprintf(f, "%d %d\n", a, b);
Funkcja fputs zapisuje do pliku
zawartość napisu.
fputs(napis, f);
Aby zapisać do pliku pojedynczy znak,
możemy użyć funkcji fputc.
fputc(znak, f);
Większy przykład
Poniższy program wypisuje na standardowe wyjście zawartość pliku podanego jako argument programu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]){ if(argc < 2){ fprintf( stderr, "Podaj nazwę pliku jako argument programu.\n" ); exit(1); } FILE * f = fopen(argv[1], "r"); if(!f){ perror("fopen"); exit(2); } int znak; while((znak = fgetc(f)) != EOF) fputc(znak, stdout); fclose(f); return 0; } |
Zadania
-
Napisz program, który wypisze liczbę znaków w pliku, którego nazwa jest podana jako parametr wywołania programu.
-
Napisz program, który jest uruchamiany z dwoma parametrami — nazwami plików. Program ma kopiować pierwszy plik do drugiego.
-
Zmodyfikuj program z poprzedniego zadania tak, aby zawartość pierwszego pliku była dopisywana do drugiego.
-
Napisz program, który jest uruchamiany z dwoma parametrami — nazwami plików. Program ma porównywać zawartość obu plików leksykograficznie. Jeśli pierwszy plik jest wcześniejszy, program ma wypisać liczbę -1. Jeśli oba pliki są jednakowe, program ma wypisać liczbę 0. Jeśli drugi plik jest wcześniejszy, program ma wypisać liczbę 1.
-
Zdefiniuj typ strukturalny
struct Miastodo przechowywania danych na temat miast: nazwy, powierzchni i ludności.Pobierz plik miasta.txt, w którym kolumny oznaczają odpowiednio: powierzchnię, ludność i nazwę miasta.
Zdefiniuj funkcję
struct Miasto *wczytane_miasto(FILE *plik), która wczyta z otwartego pliku dane na temat jednego miasta i zwróci je jako wskaźnik do dynamicznie zaalokowanej struktury typustruct Miasto. Jeśli nie udało się wczytać kolejnego miasta z powodu napotkania końca pliku, funkcja powinna zwrócić wartośćNULL.Zdefiniuj funkcję, która przyjmuje jako parametr uchwyt do otwartego pliku. Funkcja, używając funkcji
wczytane_miasto, ma wypełnić dynamiczną tablicę wskaźników do wartości typustruct Miasto. Nie znamy z góry liczby wierszy czyli miast w podanym pliku, więc funkcja powinna na bieżąco powiększać rozmiar tablicy za pomocą funkcjirealloc, aby móc zapisać wskaźniki do kolejnych wczytywanych miast. Za wskaźnikiem do ostatniego wczytanego miasta w tablicy powinna się znaleźć wartośćNULLjako znacznik końca tablicy. Funkcja ma zwrócić adres wspomnianej tablicy.Napisz funkcję, która otrzyma tablicę miast jak opisana powyżej i zwróci wskaźnik na miasto o największej gęstości zaludnienie.
Napisz funkcję, która wypisuje na standardowe wyjście informacje o mieście, które dostała jako parametr — wskaźnik na strukturę
struct Miasto.Napisz program, który, używając powyższych funkcji, wypisze miasto o największej gęstości zaludnienia.