Operacje na plikach
Praca na plikach możliwa jest dzięki funkcjom dostępnym w bibliotece stdio.h
.
Chcąc odczytać dane z pliku (lub zapisać dane do pliku) najpierw należy uzyskać dostęp
do pliku za pomocą funkcji fopen()
,
która otwiera plik i zwraca zmienną typu FILE*
.
Uzyskany w ten wskaźnik daje dostęp do pliku pozwalając na
sekwencyjny odczyt lub zapis danych do wskazanego pliku. Jest on argumentem wszystkich funkcji operujących
na plikach. Po zakończeniu pracy na pliku należy go zamknąć za pomocą funkcji fclose()
.
Otwieranie i zamykanie plików: fopen, fclose
Funkcja fopen
otwiera plik o nazwie nazwa_pliku
.
FILE *fopen(char *nazwa_pliku, char *tryb);
Pierwszy argument nazwa_pliku
jest napisem zawierającym ścieżkę do pliku. Może to być ścieżka bezwzględna
(np. w Windows "C:\\Users\a.txt"
, w Unix/Linux "/home/user/a.txt"
) lub względna zależna od bieżącego katalogu roboczego.
Jeżeli podamy tylko nazwę pliku (bez pełnej ścieżki) to powinien on się znajdować w tym samym
katalogu co plik wykonywalny.
Argument tryb
to napis, który określa tryb dostępu do pliku.
Oto lista dostępnych trybów dla plików tekstowych:
Tryb | ||
---|---|---|
"r" | Odczyt pliku od początku. Jeżeli plik nie istnieje to funkcja fopen zwróci NULL . | |
"w" | Zapis (nadpisz od początku) Jeżeli plik nie istnieje to zostanie utworzony. Jeżeli plik istnieje to jego zawartość zostanie skasowana | |
"a" | Dodaj na końcu |
Uwaga: w przypadku wystąpienia błędu przy próbie uzyskania dostępu do pliku funkcja fopen
zwraca wartość NULL
. Przyczyną błędu może być zła nazwa pliku lub niepoprawna ścieżka, brak
uprawnień do otwierania (lub edycji) pliku lub nawet brak miejsca na nośniku.
Ponieważ tego typu sytuacje mogą występować często dlatego należy zawsze sprawdzić czy
operacja fopen
zakończyła się sukcesem.
Po zakończeniu pracy na pliku należy go zamknąć za pomocą funkcji fclose
int fclose (FILE *plik);
Odczyt pojedynczego znaku ze strumienia: fgetc
Pojedynczy znak z pliku możemy pobrać za pomocą funkcji fgetc
.
int fgetc(FILE *f);
Argumentem funkcji jest wskaźnik f
do pliku uzyskany wcześniej za pomocą fopen
. Plik powinien być otworzony w trybie do odczytu.
Funkcja fgetc
zwraca wartość EOF
, gdy strumień danych z pliku jest pusty (koniec pliku).
Umieszczenie pojedynczego znaku w strumieniu: fputc
Funkcja fputc
umieszcza znak c
w pliku f
int fputc(char c, FILE* f);
Wykrywanie końca pliku: feof
Funkcja feof
pozwala sprawdzić, czy podczas odczytu pliku f
dotarliśmy do jego końca.
int feof(FILE *plik);
Funkcja zwraca wartość 1 gdy osiągnięto koniec pliku, w przeciwnym wypadku zwracana jest wartość 0.
Przykład 1: Czytanie sekwencji znaków z pliku
Poniższy program odczytuje wszystkie znaki (bajty) z pliku plik.txt
i wyświetla je na ekranie.
Jeżeli plik wejściowy nie istnieje lub gdy nastąpił jakikolwiek błąd przy otwieraniu pliku wówczas wyświetlany jest komunikat o błędzie i program kończy swoje działanie.
- znaki.c
#include<stdio.h> int main() { FILE *plik = NULL; int znak; plik = fopen( "plik.txt", "r" ); if( plik == NULL ) { printf("Wystapil blad otczytu pliku\n"); return 1; } while( feof(plik) == 0 ) { znak = fgetc(plik); if (znak != EOF) printf("%c", znak); } fclose( plik ); return 0; }
Przykładowy plik wejściowy: plik.txt. Należy umieścić go w katalogu w którym znajduje się plik wykonywalny programu.
Odczyt linii tekstu z pliku: fgets
Funkcja fgets
odczytuje linię tekstu z pliku f
i umieszcza napis w tablicy s
.
Argument drugi n
ogranicza maksymalna liczbę znaków jaką wczyta funkcja zapobiegając przepełnieniu bufora dla zbyt długich linii.
char* fgets (char *s, int n, FILE *f);
Jest to więc odpowiednik funkcji czytaj_linie
z poprzednich zajęć, który odczytuje linie z pliku.
Przykład 2: Szukanie wzorca (grep)
Poniższy program otwiera plik o nazwie abacus.txt
i wypisuje na ekranie linie, które zawierają wyraz abacus
.
- grep.c
#include<stdio.h> #include<string.h> #define MAXLINE 1000 int main() { char *wzor = "abacus"; FILE *plik = NULL; char linia[MAXLINE]; plik = fopen( "abacus.txt", "r" ); if( plik == NULL ) { printf("Wystapil blad otczytu pliku\n"); return 1; } while( feof(plik) == 0 ) { fgets(linia, MAXLINE, plik); if (strstr(linia, wzor)) printf(linia); } fclose( plik ); return 0; }
Przykładowy plik wejściowy: abacus.txt. Należy umieścić go w katalogu, w którym znajduje się plik wykonywalny programu.
Zapis formatowany do pliku tekstowego: fprintf
Funkcja fprintf
służy do zapisu tekstu do pliku z formatowaniem.
Jej działanie jest takie samo jak funkcji printf()
z jedną rożnica - formatowany tekst zamiast na ekran trafia do pliku f
otworzonego w trybie do zapisu.
int fprintf(FILE *f, char *format, ...);
Przykład 3: Zapis wartości do pliku
Poniższy program demonstruje zapis wartości całkowitej 42, wartości rzeeczywistej 4.13 oraz napisu "Witaj swiecie"
do pliku o nazwie "output.txt"
.
- zapis.c
#include<stdio.h> int main() { int a = 42; float x = 3.14; char napis[]="Witaj swiecie\n"; FILE* plik; plik = fopen("output.txt", "w"); if (plik) { fprintf(plik, "a = %d\n", a); fprintf(plik, "x = %f\n", x); fprintf(plik, "%s\n", napis); fclose(plik); } else printf("Blad zapisu do pliku\n"); return 0; }
Odczyt formatowany z pliku tekstowego: fscanf
Do odczytu z pliku danych z uwzględnieniem ich formatowania służy funkcja fscanf
. Działa ona identycznie jak znana nam już funkcja scanf
, z tą rożnica, że
odczyt danych następuje z pliku f
otworzonego w trybie do odczytu.
int fscanf(FILE *f, char *format, ...);
Przykład 4: Odczyt wartości z pliku
Poniższy program demonstruje sposób użycia funkcji fscanf
do odczytania danych zapisanych za pomocą programu z poprzedniego przykładu.'
- odczyt.c
#include<stdio.h> int main() { int a; float x; char napis[100]; FILE* plik; plik = fopen("output.txt", "r"); if (!plik) { printf("Blad odczytu do pliku\n"); return 1; } fscanf(plik, "a = %d\n", &a); fscanf(plik, "x = %f\n", &x); fgets(napis, 100, plik); fclose(plik); printf("a = %d\n", a); printf("x = %f\n", x); printf("%s\n", napis); return 0; }
Przykładowy plik z danymi do odczytu: output.txt
Więcej informacji o funkcjach służących do manipulacji na plikach znajdziesz w dokumentacji biblioteki stdio.h
Zadanie - Generuj liczby
Napisz program, który zapisze do pliku tekstowego o nazwie liczby.txt
ciąg n
losowych liczb rzeczywistych z rozkładu jednostajnego z przedziału od a
do b
.
Liczby umieszczane są w pliku w kolejnych liniach.
Do uzyskania ciągu liczb (pseudo)losowych użyj funkcję rand()
z biblioteki stdlib.h
.
Dane: liczba całkowita n
określająca ilość wygenerowanych liczb oraz dwie liczby rzeczywiste a
i b
określające przedział generowanych liczb.
Wynik: plik liczby.txt
zawierający n
losowych liczb rzeczywistych z przedziału od a
do b
umieszczonych w kolejnych liniach
Przykład
Dla danych wejściowych:
n = 10 a = -1 b = 3
program utworzy plik output.txt
zawierający 10 losowych wartości rzeczywistych z przedziału [-1, 3]
Przy każdym uruchomieniu otrzymywana jest inna losowa sekwencja liczb.
Przykładowy wynik mógłby wyglądać tak:
2.168048 0.188781 -0.507287 1.787186 2.709183 2.666946 -0.229189 -0.980685 0.169793 -0.582870
Zadanie - Szyfr - znaki przestawne
Napisz program, który utworzy w bieżącym katalogu plik o nazwie output.txt
zawierający
zaszyfrowaną treść z pliku input.txt
. Szyfrowanie wykonywane jest poprzez zamianę miejscami kolejnych par znaków (bajtów).
Pamiętaj o tym, że plik może zawierać nieparzystą liczbę znaków, wówczas ostatni znak pozostaje na swoim miejscu. W przypadku problemów z odczytaniem pliku wejściowego program wyświetla stosowny komunikat błędu i kończy swoje działanie.
Przykład
Dla pliku wejściowego input.txt
o treści:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
program utworzy plik o nawie output.txt
zawierający taką treść
oLer mpius modol ris tmate ,ocsnceetut rdapisiicgne il,ts ded oiesuom detpmroi cndidinu ttul baro eted loro eamng alaqiau.