PP1 lecture 10


Podstawy Programowania 1
Pliki
Arkadiusz Chrobot
Zakład Informatyki
17 grudnia 2015
1/69
Plan
1
Wprowadzenie
2
Obsługa plików w języku C
3
Przykłady
4
Zakończenie
2/69
Wprowadzenie
Wprowadzenie
Złożone programy komputerowe potrafią przetwarzać i generować ogromne
ilości danych. Te informacje nie zawsze mieszczą się w pamięci operacyjnej
komputera (ram). Co więcej, po wyłączeniu zasilania zawartość tej pamięci
zanika. Aby w sposób trwały przechowywać takie ilości informacji opracowa-
no urządzenia pamięci masowej. Sposób przechowywania przez nie informacji
zależy od ich typu. Aby ujednolicić sposób dostęp do danych gromadzonych
w różnych rodzajach takiej pamięci stworzono specjalną strukturę danych,
którą nazywa się plikiem.
3/69
Wprowadzenie
Praca z plikiem
Pracę programu z plikiem można opisać następującym schematem bloko-
wym:
start
otwarcie pliku
odczyt/zapis obsługa wyjątków
zamknięcie pliku
stop
Wyjątek jest każdą sytuacją, która wymaga odmiennego potraktowania przez
program, bo może potencjalnie prowadzić do błędów przetwarzania. Przy-
kładem wyjątku jest brak pliku, który program ma otworzyć.
4/69
Obsługa plików w języku C
Obsługa plików w języku C
Język C oferuje dla programistów dwie możliwości obsługi plików: niskopo-
ziomową, związaną z tzw. deskryptorami plików i wysokopoziomową dostar-
czaną za pomocą tzw. strumieni. W ramach wykładu zostanie przedstawiona
obsługa wysokopoziomowa, która gwarantuje między innymi automatyczne
buforowanie informacji odczytywanych i z zapisywanych do pliku. Strumień
jest zmienną typufile*1. Wszystkie funkcje związane z obsługą strumieni
są zadeklarowane w pliku nagłówkowym stdio.h, który należy włączyć do
programu.
1
Dokładniej jest to wskaznik na strukturę o nazwiefile.
5/69
Obsługa plików w języku C
Standardowe strumienie
Zmienne typufile * służą w języku C nie tylko do obsługi plików. Mają
one jeszcze kilka innych zastosowań. Po włączeniu do programu pliku na-
główkowego stdio.h stają się dostępne trzy zmienne tego typu o nazwach
stdin,stdoutistderr. Pierwsza jest nazwana standardowym strumieniem
wejściowym i jest domyślnie związana z klawiaturą, druga to standardowy
strumień wyjścia i jest związana domyślnie z ekranem, trzecia to wyjście
diagnostyczne i jest również domyślnie związana z ekranem. Do strumienia
diagnostycznego programy powinny kierować komunikaty związane z poja-
wiającymi się wyjątkami lub błędami.
6/69
Obsługa plików w języku C
Otwieranie pliku
Otwieranie pliku jest czynnością rozpoczynającą pracę programu z plikiem.
W języku C pozwala ona powiązać plik ze zmienną strumieniową i wykony-
wana jest poprzez wywołanie funkcji fopen(), która przyjmuje dwa argu-
menty. Pierwszym jest łańcuch znaków będący ścieżką do otwieranego pliku,
a drugim ciąg, który może się składać z następujących znaków:
Tryb Opis
r Otwarcie tylko do odczytu. Wskaznik pliku wskazuje
na jego początek.
r+ Otwarcie do odczytu i zapisu. Wskaznik pliku wska-
zuje na jego początek.
w Utworzenie pliku i otwarcie tylko do zapisu. Jeśli plik
o podanej nazwie istnieje, to jego zawartość jest ka-
sowana. Wskaznik pliku wskazuje na jego początek.
w+ Jak wyżej, ale z możliwością odczytu.
a Otwarcie do dopisywania. Wskaznik pliku wskazuje na
jego koniec. Jeśli plik nie istnieje, to będzie utworzony.
7/69
Obsługa plików w języku C
Otwieranie pliku
Kontynuacja
Tryb Opis
a+ Jak wyżej, ale z możliwością odczytu.
b Otwarcie będzie dotyczyło pliku binarnego. Obecnie
większość systemów operacyjnych ignoruje ten znacz-
nik.
Znaki z tabeli mogą być łączone ze sobą, o ile nie wykluczają się wzajemnie
(np. otwarcie do zapisu i dopisywania nie mogą być użyte razem). Znacznik
b informował funkcję, że otwierany plik będzie plikiem binarnym, a nie tek-
stowym. Obecnie tylko niektóre systemy operacyjne robią takie rozróżnienie.
Wynik działania funkcji fopen() należy przypisać do zmiennej typufile
*. Jeśli ta zmienna po wykonaniu czynności otwierania pliku będzie miała
wartośćnull, to oznacza to, że wystąpił wyjątek i z jakiś przyczyn nie udało
się otworzyć pliku.
8/69
Obsługa plików w języku C
Obsługa wyjątków
Jeśli funkcja fopen() zasygnalizowała wyjątek, to można poznać kod tego
wyjątku za pomocą wywołania funkcji ferror(). Przyjmuje ona jako argu-
ment wywołania zmienną typufile *, a zwraca kod błędu lub zero, jeśli
nie było żadnego wyjątku. Ten kod może zostać wyzerowany przy pomocy
wywołania funkcji clearerr(), ale należy jej używać ostrożnie, bo zeruje
ona również znacznik końca pliku. Podobnie jak ferror() przyjmuje ona
jako argument wywołania strumień. Obie funkcje działają nie tylko dla wy-
jątków zgłaszanych przez fopen(), ale również przez inne funkcje operujące
na strumieniach. Opis słowny (w języku zależnym od konfiguracji kompute-
ra) można otrzymać za pomocą funkcji perror(). Należy ją wywołać tuż
po zakończeniu funkcji, jeśli zgłosiła ona wyjątek i jako argument wywołania
przekazać jej nazwę tej funkcji w postaci łańcucha znaków.
9/69
Obsługa plików w języku C
Zapis do pliku
Zapis do pliku tekstowego
Zapis do pliku tekstowego2 możemy zrealizować przy pomocy funkcjifputc(),
która przyjmuje dwa argumenty wywołania - kod ascii zapisywanego znaku
(jako wartość typu int) oraz strumień związany z plikiem do którego na-
stąpi zapis. Zwraca ona kod zapisanego znaku lub stałą o nazwieeof(ang.
End Of File) w przypadku niepowodzenia. Cały łańcuch znaków można za-
pisać do pliku przy pomocy funkcji fpus(), która przyjmuje dwa argumenty
wywołania - tablicę znaków, z ciągiem do zapisania i strumień. Funkcja ta
zwraca liczbę zapisanych do pliku znaków lub opisaną wyżej stałą w razie
niepowodzenia.
2
Plik tekstowy to taki plik, który zawiera wartości zapisane w postaci kodów
ascii, a więc można jego zwartość przeczytać i edytować dowolnym edytorem
tekstowym.
10/69
Obsługa plików w języku C
Zapis do pliku
Zapis do pliku tekstowego - kontynuacja
Dane różnych typów można skonwertować do łańcucha znaków i zapisać je
w pliku tekstowym za pomocą funkcji fprintf(), która wywoływana jest
podobnie do funkcji printf(), ale jako pierwszy argument przyjmuje stru-
mień związany z plikiem, do którego mają trafić dane. Funkcja fprintf()
zwraca liczbę znaków, które udało się zapisać w pliku3. Jest ona także sto-
sowana do zapisu komunikatów o wyjątkach do standardowego wyjścia dia-
gnostycznego.
3
Ciekawostką jest to, że funkcja printf() zwraca liczbę wypisanych na ekran
znaków, ale zazwyczaj ta informacja jest ignorowana w programie.
11/69
Obsługa plików w języku C
Zapis do pliku
Zapis do pliku binarnego
Jeśli chcemy zapisać informację do pliku binarnego4 to możemy w tym ce-
lu użyć funkcji fwrite(), która przyjmuje cztery argumenty wywołania -
- wskaznik na zmienną, która zawiera informację do zapisania5, rozmiar po-
jedynczej porcji danych zapisywanych z bufora, liczbę tych porcji i strumień.
Ta funkcja zwraca liczbę faktycznie zapisanych porcji. Jeśli jest ona mniejsza
od wartości przekazanej jako jej trzeci argument wywołania, to znaczy, że
wystąpił wyjątek - błąd zapisu.
4
W pliku binarnym wartości są zapisywane w takiej samej postaci, jak w pa-
mięci operacyjnej, czyli jako ciąg bitów, które niekoniecznie są kodami ascii.
5
Taką zmienną nazywa się buforem.
12/69
Obsługa plików w języku C
Odczyt z pliku
Podstawowym problemem podczas odczytu z pliku jest określenie kiedy skoń-
czą się w nim dane. W języku C, w przypadku obsługi plików za pomocą
strumieni, o osiągnięciu końca pliku informuje funkcja feof(). Przyjmuje
ona jako argument wywołania strumień, a zwraca liczbę typy int. Jeśli ta
liczba jest różna od zera, to znaczy, że w pliku nie ma więcej danych i na-
leży przerwać jego odczytywanie. Wywołanie tej funkcji jest często używane
w warunkach pętli, co zostanie zademonstrowane w przykładowych progra-
mach.
13/69
Obsługa plików w języku C
Odczyt z pliku
Odczyt z pliku tekstowego
Pojedyncze znaki z pliku tekstowego można odczytywać przy pomocy funk-
cji fgetc(), która przyjmuje jako argument wywołania strumień, a zwraca
kod ascii odczytanego znaku jako liczbę typu int lub stałąeofw przypad-
ku wystąpienia wyjątku lub w przypadku osiągnięcia końca pliku. Odczytu
łańcucha znaków z pliku można dokonać z użyciem funkcji fgets(), któ-
ra przyjmuje trzy argumenty - tablicę znaków, w której będzie umieszczony
odczytany z pliku ciąg znaków, rozmiar tej tablicy (funkcja odczytuje od-
czytuje o jeden znak mniej niż wynosi ten rozmiar) oraz strumień, z którego
ma być dokonany odczyt. Jeśli dane różnych typów zostały zapisane z uży-
ciem funkcji fprintf(), to można je odczytać używając funkcji fscanf(),
która musi być z użyciem tego samego ciągu formatującego. Jej pierwszym
argumentem jest ten sam strumień, co w przypadku fprintf(). Pozostałe
argumenty są wskaznikami na zmienne dla danych. Funkcja ta zwraca liczbę
elementów pliku, które udało się dopasować do ciągu formatującego.
14/69
Obsługa plików w języku C
Odczyt z pliku
Odczyt z pliku binarnego
Dane zapisane przy pomocy funkcji fwrite() można odczyta z pliku binar-
nego za pomocą funkcji fread(). Przyjmuje ona takie same argumenty, jak
funkcja wspomniana wcześniej, z tym, że w jej przypadku dane są odczyty-
wane ze strumienia i umieszczane w zmiennej przekazanej jej przez pierwszy
argument wywołania. Zwraca liczbę liczbę porcji danych, które faktycznie
udało się jej odczytać z pliku. Jeśli ta liczba będzie mniejsza od liczby prze-
kazanej jej jako trzeci argument wywołania, to oznacza to, że w pliku nie
ma już więcej danych do odczytu.
15/69
Obsługa plików w języku C
Zamykanie pliku
Funkcją służącą do zamykania pliku jest fclose(). Jak argument wywoła-
nia przyjmuje ona strumień związany z plikiem, który ma zostać zamknięty.
Zwraca ona wartość różną od zera, jeśli jej działanie zakończyło się niepo-
wodzeniem.
16/69
Obsługa plików w języku C
Inne funkcje do obsługi plików
Z każdym plikiem obsługiwanym za pomocą strumieni związany jest wskaz-
nik pliku, który pełni podobną rolę jak indeks w tablicy i o którym wspo-
mniano w tabeli znaczników trybów dostępu dla funkcji fopen(). Funk-
cje zapisujące i odczytujące pliki niejawnie (automatycznie) zwiększają jego
wartość. Taki rodzaj dostępu do danych, w który wartość wskaznika jest
wyłącznie zwiększana nazywamy sekwencyjnym. Sposób swobodny lub ina-
czej bezpośredni umożliwia zarówno zwiększanie, jak i zmniejszanie wskaz-
nika pliku, a więc posługiwanie się plikiem podobnie do tablicy. Modyfika-
cja wartości wskaznika pliku wykonywana jest za pomocą funkcji fseek(),
która przyjmuje trzy argumenty wywołania. Pierwszym jest strumień zwią-
zany z plikiem, którego wskaznik ma być przesunięty, drugim jest liczba
bajtów (typu long), o którą ma być przesunięty, a trzecim jedna z trzech
stałych:seek_set- przesunięcie będzie liczone względem początku pliku,
seek_curr- przesunięcie będzie liczone względem bieżącej pozycji wskaz-
nika,set_end- przesunięcie będzie liczone względem końca pliku. Wyjątek
jest sygnalizowany przez funkcję zwróceniem wartości -1.
17/69
Obsługa plików w języku C
Inne funkcje do obsługi plików
Funkcja rewind() przesuwa wskaznik pliku na jego początku. Jako argu-
ment przyjmuje strumień i nie zwraca żadnej wartości. Funkcja ftell()
również przyjmuje strumień jako argument wywołania i zwraca bieżącą war-
tość wskaznika pliku (liczba typu long). Do manipulowania wskaznikiem
pliku można użyć także funkcji fgetpos() i fsetpost(), które nie będą
jedna tutaj szerzej opisywane.
18/69
Obsługa plików w języku C
Inne funkcje do obsługi plików
Podczas zapisu dane nie zawsze trafiają bezpośrednio do pliku, ale mogą
być umieszczone w pamięci operacyjnej w buforze, który program niejawnie
przeznacza na nie. Dzieje się tak dlatego, że operacja zapisu danych do
urządzenia pamięci masowej trwa stosunkowo długo, więc aby nie blokować
swojego działania program odkłada ją na pózniej. Aby opróżnić te bufory
można użyć funkcji fflush(), która przyjmuje jako argument wywołania
strumień, a zwraca zero w przypadku powodzenia wykonywanej przez nią
czynności lub stałą eof jeśli wystąpił wyjątek. Opróżnienie tych buforów nie
gwarantuje jednak zapisu danych na nośnik, bo buforowanie może stosować
także system operacyjny, który nadzoruje wykonanie programu. Bufory te są
automatycznie opróżniane po wywołaniu fclose(). Standardowa biblioteka
języka C dostarcza także innych funkcji do zarządzania tymi buforami, ale
nie będą one na tym wykładzie omawiane.
19/69
Obsługa plików w języku C
Funkcje zarządzające plikami
W standardowej bibliotece języka C zostały zdefiniowane także funkcje, któ-
re służą do zarządzania plikami. Opisane zostaną tu dwie z nich. Funkcja
remove() (w uproszczeniu) służy do usuwania plików lub katalogów. Ja-
ko argument wywołania przyjmuje ona łańcuch znaków będący nazwą usu-
wanego pliku (katalog). Jeśli wystąpi wyjątek, to funkcja ta zwróci liczbę
-1. Funkcja rename() zmienia nazwę pliku lub jego lokalizację na nośniku
danych. Przyjmuje ona dwa argumenty. Pierwszym jest stara nazwa (lo-
kalizacja) pliku, a drugim nowa nazwa (lokalizacja). Funkcja zwraca zero,
jeśli czynność zmiany nazwy (lokalizacji) się powiodła lub -1 w przeciwnym
przypadku.
20/69
Przykłady
Przykład pierwszy
Zapis pojedynczych znaków do pliku tekstowego
Jako pierwszy przykład zostanie zaprezentowany program, który zapisuje
100 wylosowanych małych liter do pliku tekstowego, a następnie je z niego
odczytuje i wypisuje na ekran. W programie zaprezentowano także jeden
z możliwych sposobów wykrywania i informowania o wyjątkach. Ich obsłu-
ga nie jest jednak pełna, np. otwarty plik nie jest zamykany, jeśli nastąpił
wyjątek jego zapisu.
21/69
Przykłady
Przykład pierwszy
#include
#include
#include
#define LENGTH 100
22/69
Przykłady
Przykład pierwszy
Komentarz
Fragment kodu zródłowego programu zaprezentowany na poprzednim slaj-
dzie zawiera instrukcje włączające pliki nagłówkowe. Oprócz pliku stdio.h
włączane są także pliki stdlib.h oraz time.h ponieważ program będzie
korzystał z generatora liczb pseudolosowych. Stała LENGTH określa liczbę
elementów tablicy znaków, w której będzie zapisana nazwa pliku.
23/69
Przykłady
Pierwszy przykład
void display_exception_message(int code)
{
char exception_description [][LENGTH] = {
"Błąd otwarcia pliku do zapisu.",
"Błąd zapisu pliku.",
"Błąd zamknięcia pliku.",
"Błąd otwarcia pliku do odczytu."
};
fprintf(stderr,"%s\n",exception_description[-code-1]);
}
24/69
Przykłady
Przykład pierwszy
Komentarz
Zaprezentowana na poprzednim slajdzie funkcja będzie służyła do wypisy-
wania na ekran komunikatów związanych z wyjątkami, które mogą pojawić
się podczas działania innych funkcji zawartych w tym programie. Treści tych
komunikatów zawarte są w tablicy exception_description. Do ich wypi-
sania używana jest funkcja fprintf(), która jako pierwszy argument przyj-
muje strumień diagnostyczny. Proszę zwrócić uwagę na wyliczanie indeksu
dla tablicy komunikatów. Ponieważ wyjątki będą sygnalizowane ujemnymi
liczbami, poczynając od -1, to aby otrzymać prawidłowy indeks zmieniany
jest znak kodu błędu i odejmowana jest od niego liczba jeden. Tak więc kod
-1 będzie oznaczał wyjątek związany z otwarciem pliku, -2 z zapisem do
pliku itd.
25/69
Przykłady
Przykład pierwszy
int fill_file(char *file_name)
{
FILE *file = NULL;
srandom(time(0));
file = fopen(file_name,"w");
if(file==NULL)
return -1;
int i;
for(i=0; i<100; i++)
if(fputc('a'+random()%('z'-'a'+1),file)==EOF)
return -2;
if(fclose(file)!=0)
return -3;
return 0;
}
26/69
Przykłady
Przykład pierwszy
Komentarz
Funkcja fill_file() odpowiedzialna jest za zapis stu małych liter do pli-
ku tekstowego. Posiada ona jeden parametr, przez który przekazywana jest
nazwa pliku do zapisu, a jako jej wartość zwracany jest kod wyjątku. W przy-
padku gdy będzie on równy zero będzie to oznaczało, że wszystkie czynności
wykonywane przez funkcję zakończyły się prawidłowo. Jeśli będzie różny od
zera, to znaczy, że wystąpił gdzieś wyjątek. Na początku funkcja inicjuje
zmienną strumieniową, a następnie generator liczb pseudolosowych. Następ-
nie otwiera ona plik do zapisu. Jeśli on nie istniał to jest tworzony, w prze-
ciwnym przypadku kasowana jest jego dotychczasowa zawartość. Jeżeli nie
powiodło się otwarcie pliku, to funkcja zwraca liczbę -1 i kończy swoje dzia-
łania. Jeśli jednak ta operacja przebiegła prawidłowo, to funkcja w pętli
losuje sto małych liter i zapisuje je do pliku, za każdym razem badając, czy
nie wystąpił wyjątek zapisu. Jeśli by się tak stało, to funkcja przerwie swoje
wykonanie i zwróci liczbę -2. Po zakończeniu pętli funkcja zamyka plik. Jeśli
ta czynność się nie powiedzie, to jest to sygnalizowane przez nią zwróceniem
wartości -3. Funkcja kończy się zwracając liczbę 0.
27/69
Przykłady
Przykład pierwszy
int read_file(char *file_name)
{
FILE *file = fopen(file_name,"r");
if(file==NULL)
return -4;
while(!feof(file)) {
char data_from_file = fgetc(file);
if(data_from_file!=EOF)
printf("%c ",data_from_file);
}
if(fclose(file)!=0)
return -3;
return 0;
}
28/69
Przykłady
Pierwszy przykład
Komentarz
Funkcja read_file() odczytuje wszystkie znaki z pliku, którego nazwa jest
jej przekazywana przez parametr i wyświetla je na ekran. Najpierw funkcja
otwiera plik do odczytu. Jeśli ta operacja się nie powiedzie, to zwraca ona
wartość-4i kończy swoje działanie. W przeciwnym przypadku w pętliwhile
odczytuje z użyciem funkcji fgetc znak po znaku plik i wypisuje te znaki na
ekranie. Zapis !feror(file) w warunku pętli jest skróconą wersją zapisu
ferror(file)==0. W pętli dodatkowo badane jest, czy fgetc() nie zwró-
ciła wartościeof. Ta wartość sygnalizuje, że przeczytany został znak końca
pliku i nie trzeba nic już wypisywać na ekranie. Po zakończeniu pętli funkcja
zamyka plik. Jeśli nie uda się wykonać prawidłowo tej czynności, to funkcja
read_file() przerywa swoje działanie i zwraca ten sam kod wyjątku, co
funkcja fill_file(). Jeśli wszystkie czynności wewnątrz funkcji przebiegły
prawidłowo, to kończąc się zwraca ona liczbę zero.
29/69
Przykłady
Przykład pierwszy
int main(void)
{
int result = fill_file("test.txt");
if(result<0)
display_exception_message(result);
result = read_file("test.txt");
if(result<0)
display_exception_message(result);
return 0;
}
30/69
Przykłady
Przykład pierwszy
Komentarz
W funkcji main() najpierw wywoływana jest funkcja fill_file(), a na-
stępnieread_file(). Wynik wykonania każdej z nich zapisywany jest w zmien-
nej result. Jeśli będzie on ujemny, to wówczas wywoływana jest funkcja
display_exception_message(), do której przekazywana jest wartość ko-
du wyjątku.
31/69
Przykłady
Przykład drugi
Drugi program pozwala zapisać do pliku tekstowego dziesięć łańcuchów zna-
ków, które mogą zaiwerać spacje i inne znaki. Te ciągi znaków wprowadzane
są przez użytkownika do momentu, aż nie poda on wyrazu  stop . Podobnie
jak w poprzednim programie obsługa wyjątków jest tylko częściowa.
32/69
Przykłady
Przykład drugi
#include
#include
#define LENGTH 100
#define SENTENCE_LENGTH 81
33/69
Przykłady
Przykład drugi
Komentarz
Oprócz pliku nagłówkowego stdio.h do programu włączany jest również
plik string.h, ponieważ w programie używane są funkcje realizujące ope-
racje na łańcuchach znaków. Znaczenie stałejlengthjest takie samo jak
w poprzednim programie. Stałasentence_lengthdefiniuje wielkość tablicy
znaków w jakiej będą zapamiętane ciągi znaków zapisywane w pliku. Osiem-
dziesiąt to najczęściej spotykana standardowa maksymalna liczba znaków
w wierszu ekranu.
34/69
Przykłady
Przykład drugi
void display_exception_message(int code)
{
char exception_description [][LENGTH] = {
"Błąd otwarcia pliku do zapisu.",
"Błąd zapisu pliku.",
"Błąd zamknięcia pliku.",
"Błąd otwarcia pliku do odczytu."
};
fprintf(stderr,"%s\n",exception_description[-code-1]);
}
35/69
Przykłady
Przykład drugi
Komentarz
Funkcjaexception_message()jest zaimplementowana dokładnie tak samo
jak w poprzednim programie.
36/69
Przykłady
Przykład drugi
int write_sentences_to_file(char *file_name)
{
char sentence[SENTENCE_LENGTH] = "\0";
FILE *file=fopen(file_name,"w");
if(file==NULL)
return -1;
while(strncmp(sentence,"stop",SENTENCE_LENGTH-1)!=0)
{
scanf("%80[^\n]s",sentence);
while(getchar()!='\n');
if(fprintf(file,"%s\n",sentence)!=strlen(sentence)+1)
return -2;
}
if(fclose(file)!=0)
return -3;
return 0;
}
37/69
Przykłady
Przykład drugi
Komentarz
Funkcja write_sentences_to_file() zapisuje pobrane od użytkownika
ciągi znaków do pliku, którego nazwa jest przekazana jej przez parametr.
W funkcji deklarowana jest zmienna lokalna o nazwie sentence, która jest
tablicą znaków. Jest ona inicjowana pustym łańcuchem znaków. Funkcja
najpierw otwiera plik do zapisu. Jeśli ta operacja zakończy się sukcesem to
w pętli pobierany jest od użytkownika łańcuch znaków i zapisywany do pli-
ku za pomocą funkcji fprintf(). Kontrola poprawności zapisu polega na
sprawdzeniu liczby znaków, które ta funkcja zapisała z liczbą znaków zapi-
sywanego ciągu. Wewnętrzna pętla while służy do usunięcia znaku nowego
wiersza (klawisza Enter) ze standardowego wejścia, aby funkcja scanf()
mogła prawidłowo pobrać kolejny ciąg od użytkownika. Po wprowadzeniu
przez niego wyrazu  stop pętla jest kończona, a plik jest zamykany. Jeśli
któraś z operacji na pliku zakończy się niepowodzeniem to funkcja przerywa
swoje działanie i zwraca kod wyjątku. Jeśli wszystkie czynności zakończą się
pomyślnie, to funkcja kończąc się zwróci liczbę zero.
38/69
Przykłady
Przykład drugi
nt read_sentences_from_file(char *file_name)
{
char sentence[SENTENCE_LENGTH] = "\0";
FILE *file = fopen(file_name,"r");
if(file==NULL)
return -4;
while(feof(file)==0) {
fscanf(file,"%[^\n]s",sentence);
if(feof(file)==0) {
puts(sentence);
while(fgetc(file)!='\n');
}
}
if(fclose(file)!=0)
return -3;
return 0;
}
39/69
Przykłady
Przykład drugi
Komentarz
Funkcja read_sentence_from_file() odczytuje wiersz po wierszu zapi-
sane w pliku tekstowym przez poprzednio opisywaną funkcję ciągi znaków
i wypisuje je na ekranie. Przez parametr przekazywana jest jej nazwa pliku
tekstowego. Podobnie jak w jej poprzedniczce jest w niej zadeklarowana lo-
kalna tablica znaków. Funkcja najpierw otwiera plik do odczytu, a następnie
w pętli odczytuje kolejne wiersze pliku i wypisuje je na ekran. Po każdym wy-
konaniu funkcji fscanf(), jeśli nie wystąpił jeszcze znacznik końca pliku,
wywoływana jest w wewnętrznej pętli funkcja fgetc(), celem odczytania
znaków końca wiersza i przesunięcia wskaznika pliku na właściwe dane, któ-
re będą odczytywane przez fscanf() w kolejnej iteracji zewnętrznej pętli.
Po zakończeniu odczytu funkcja zamyka plik. Podobnie jak w przypadku
poprzednio opisywanej funkcji każdy wyjątek powoduje zakończenie przez
funkcję działania i zwrócenie odpowiedniego kodu wyjątku. Pomyślne ukoń-
czenie działania funkcja sygnalizuje zwracając liczbę zero.
40/69
Przykłady
Przykład drugi
int main(void)
{
int result = write_sentences_to_file("test.txt");
if(result!=0)
display_exception_message(result);
result = read_sentences_from_file("test.txt");
if(result!=0)
display_exception_message(result);
return 0;
}
41/69
Przykłady
Przykład drugi
Komentarz
Funkcja main() jest zaimplementowana podobnie, jak w poprzednio opisy-
wanym programie.
42/69
Przykłady
Przykład trzeci
Trzeci program zapisuje do pliku binarnego struktury zawierające wylosowane
współrzędne punktów w trójwymiarowym układzie kartezjańskim. Również
i tym razem obsługa wyjątków jest niepełna, aby nie komplikować zbytnio
zapisu programu. Dodatkowo w programie zostanie zaprezentowana operacja
dopisywania danych na końcu istniejącego pliku.
43/69
Przykłady
Przykład trzeci
#include
#include
#include
#define LENGTH 100
44/69
Przykłady
Przykład trzeci
Komentarz
Początek programu jest taki sam, jak w przypadku programu pierwszego.
45/69
Przykłady
Przykład trzeci
void display_exception_message(int code)
{
char exception_description[][LENGTH] = {
"Błąd otwarcia pliku do zapisu.",
"Błąd zapisu pliku.",
"Błąd zamknięcia pliku.",
"Błąd otwarcia pliku do dopisywania.",
"Błąd dopisywania do pliku",
"Błąd otwarcia pliku do odczytu"
};
fprintf(stderr,"%s\n",exception_description[-code-1]);
}
46/69
Przykłady
Przykład trzeci
Komentarz
Tablicaexception_descriptionw funkcjidisplay_exception_message()
zawiera więcej elementów niż jej odpowiedniczki z poprzednich programów.
Dodatkowe komunikaty związane są z wyjątkami, jakie mogą pojawić się
podczas operacji dopisywania danych do istniejącego pliku.
47/69
Przykłady
Przykład trzeci
struct coordinates {
double x,y,z;
};
48/69
Przykłady
Przykład trzeci
Komentarz
Zaprezentowany na poprzednim slajdzie typ strukturalny definiuje struktury,
które będą zapisywane do pliku binarnego.
49/69
Przykłady
Przykład trzeci
int write_to_file(char *file_name)
{
FILE *file = fopen(file_name,"w");
if(file==NULL)
return -1;
int i;
for(i=0; i<10; i++) {
const int RANGE = 20;
struct coordinates point;
point.x = random()%RANGE;
point.y = random()%RANGE;
point.z = random()%RANGE;
if(fwrite(&point,sizeof(point),1,file)!=1)
return -2;
}
if(fclose(file)!=0)
return -3;
return 0;
50/69
}
Przykłady
Przykład trzeci
Komentarz
Funkcja write_to_file() wypełnia pola struktury zadeklarowanej jako
zmienna lokalna liczbami wylosowanymi z zakresu od 0 do 19, a następnie
zapisuje tę strukturę do pliku przy wywołując w tym celu funkcję fwrite().
Czynność tę powtarza 10 razy. Poprawność zapisu danych sprawdzana jest
poprzez zbadanie, czy funkcja fwrite() zwróciła taką samą liczbę, jaka
jej została przekazana jako trzeci argument wywołania. Ta liczba określa ile
porcji danych będzie jednorazowo zapisywanych do pliku i w opisywanym
przypadku jest to jedna porcja. Przed rozpoczęciem zapisu plik jest otwiera-
ny, a po jego zakończeniu zamykany. Jeśli podczas wykonywania dowolnej
z czynności związanych z obsługą pliku pojawi się wyjątek, to funkcja prze-
rwie swoje działanie i zwróci liczbę ujemną. W przypadku, gdy wszystkie
operacje zakończą się sukcesem funkcja zwróci liczbę zero.
51/69
Przykłady
Przykład trzeci
int add_to_file(char *file_name)
{
FILE *file = fopen(file_name,"a");
if(file==NULL)
return -4;
struct coordinates point;
const int RANGE = 40;
point.x = random()%RANGE;
point.y = random()%RANGE;
point.z = random()%RANGE;
if(fwrite(&point,sizeof(point),1,file)!=1)
return -5;
if(fclose(file)!=0)
return -3;
return 0;
}
52/69
Przykłady
Przykład trzeci
Komentarz
Funkcja add_to_file() podobna jest w działaniu do funkcji opisanej po-
przednio, ale występuje między nimi kilka znaczących różnic. Tym razem
plik nie jest otwierany do zapisu, a do dopisywania, co oznacza, że jeśli plik
istniał, to jego zawartość nie jest kasowana, a wskaznik tego pliku usta-
wiany jest na jego końcu. Zapisywana jest tylko jedna struktura do pliku,
a wartości dla jej pól losowane są z zakresu od 0 do 39. Kontrola i obsługa
wyjątków, oraz pozostałe operacje zrealizowane są podobnie jak w funkcji
write_to_file().
53/69
Przykłady
Przykład trzeci
int read_from_file(char *file_name)
{
FILE *file = fopen(file_name,"r");
if(file==NULL)
return -6;
int i=0;
struct coordinates point;
while(fread(&point,sizeof(point),1,file)==1)
printf("Punkt %d -- x: %lf, y: %lf z: %lf\n",
++i,point.x, point.y, point.z);
if(fclose(file)!=0)
return -3;
return 0;
}
54/69
Przykłady
Przykład trzeci
Komentarz
Funkcja read_from_file() otwiera do odczytu plik binarny, którego nazwa
została jej przekazana przez parametr, a następnie odczytuje w pętli wszyst-
kie struktury z tego pliku i wypisuje zwartość ich pól na ekran w osobnych
wierszach. Po zakończeniu odczytu funkcja zamyka plik. Wykrywanie końca
pliku tym razem zostało zrealizowane inaczej niż w poprzednich programach.
Badane jest co zwróciło wywołanie funkcji fread(). Dopóki zwraca ona in-
formację, że udało jej się odczytać jedną (kolejną) strukturę, dotąd pętla
jest wykonywana. W przeciwnym przypadku wykonanie pętli jest kończone.
Obsługa wyjątków zrealizowana jest podobnie jak w poprzednio opisywanych
funkcjach.
55/69
Przykłady
Przykład trzeci
int main(void)
{
srandom(time(0));
int result = write_to_file("dane.bin");
if(result!=0)
display_exception_message(result);
result = add_to_file("dane.bin");
if(result!=0)
display_exception_message(result);
result = read_from_file("dane.bin");
if(result!=0)
display_exception_message(result);
return 0;
}
56/69
Przykłady
Przykład trzeci
Komentarz
Funkcja main() zdefiniowana jest analogicznie jak w poprzednio opisywa-
nych programach. Nowym elementem jest wywołanie funkcji, która dopisuje
nowe dane (nową strukturę) na końcu istniejącego już pliku. W tej funkcji
jest także inicjowany generator liczb pseudolosowych.
57/69
Przykłady
Przykład czwarty
Czwarty, ostatni przykładowy program zapisuje do pliku tekstowego wyloso-
wane małe litery i liczby naturalne wierszami, po cztery wartości oddzielone
od siebie spacjami. W programie zastosowano trochę inne podejście do ob-
sługi wyjątków, które jest skuteczniejsze od zaprezentowanych poprzednio,
ale też nie jest doskonałe, bo funkcja odczytująca plik nie jest informowana
o tym, czy jego utworzenie się powiodło.
58/69
Przykłady
Przykład czwarty
#include
#include
#include
#define LENGTH 50
59/69
Przykłady
Przykład czwarty
Komentarz
Do programu włączane są te same pliki nagłówkowe, co w poprzednim pro-
gramie. Zdefiniowana stała o nazwielengthokreśla liczbę elementów w ta-
blicy znaków będącej parametrem, przez który do funkcji zapisujących i od-
czytujących dane przekazywana jest nazwa pliku.
60/69
Przykłady
Przykład czwarty
void write_to_file(char file_name[LENGTH])
{
srandom(time(0));
FILE *file = fopen(file_name,"w");
if(file!=NULL) {
int i;
for(i=0; i<5; i++) {
fprintf(file,"%c %ld %ld %ld\n",
(char)('a'+random()%('z'-'a'+1)),
random()%5, random()%7, random()%3);
if(ferror(file)) {
perror("fprintf()");
break;
}
}
if(fclose(file))
perror("fclose()");
}
61/69
}
Przykłady
Przykład czwarty
Komentarz
Funkcja write_to_file(), po zainicjowaniu generatora liczb pseudoloso-
wych, otwiera do zapisu plik tekstowy, którego nazwa jest jej przekazana
przez parametr, a następnie zapisuje do niego pięć wierszy, z których każdy
zawiera wylosowaną małą literę oraz trzy liczby naturalne losowane z różnych
zakresów. Proszę zwrócić uwagę, że zapis nie jest wykonywany, jeśli nie uda-
ło się otworzyć pliku. Kontrola poprawności zapisu jest dokonywana poprzez
wywoływanie funkcji ferror() po każdym wywołaniu funkcji fprintf().
Po zakończeniu zapisu, niezależnie od tego czy wystąpił wyjątek, czy nie
plik jest zamykany, ale, ponownie, tylko wtedy gdy wcześniej udało się go
otworzyć. Komunikaty o ewentualnych wyjątkach są wypisywane na ekranie
monitora z użyciem funkcji perror().
62/69
Przykłady
Przykład czwarty
void read_from_file(char file_name[LENGTH])
{
char letter;
long int first_number, second_number, third_number;
FILE *file = fopen(file_name,"r");
if(file!=NULL) {
while(!feof(file)) {
fscanf(file,"%c %ld %ld %ld\n",&letter,&first_number,
&second_number,&third_number);
if(ferror(file)) {
perror("fscanf()");
break;
}
printf("%c %ld %ld %ld\n",letter,first_number,
second_number,third_number);
}
if(fclose(file))
perror("fclose()");
}
}
63/69
Przykłady
Przykład czwarty
Komentarz
Funkcja read_from_file() zgodnie ze swoją nazwą odczytuje zwartość
pliku, którego nazwa została jej przekazana przez parametr i wypisuje ją na
ekran. Plik jest najpierw otwierany do odczytu, następnie w pętli dopóki są
w nim dane jest odczytywany wiersz po wierszu za pomocą funkcjifscanf().
Proszę zwrócić uwagę, że ciąg formatujący jest identyczny jak dla funkcji
fprintf(), która zapisywała dane do pliku. Również obsługa wyjątków jest
zorganizowana podobnie jak w funkcji opisywanej poprzednio.
64/69
Przykłady
Przykład czwarty
int main(void)
{
write_to_file("liczby.txt");
read_from_file("liczby.txt");
return 0;
}
65/69
Przykłady
Przykład czwarty
Komentarz
Funkcja main() w opisywanym programie jest dosyć prosta. Oprócz typo-
wych elementów zawiera jedynie wywołania dwóch opisywanych wcześniej
funkcji. Kody ich wykonania nie jest badany, ponieważ one nic nie zwracają.
66/69
Zakończenie
Podziękowania
Składam podziękowania dla dra inż. Grzegorza Aukawskiego i mgra inż.
Leszka Ciopińskiego za udostępnienie materiałów, których fragmenty zostały
wykorzystane w tym wykładzie.
67/69
Zakończenie
Pytania
?
68/69
Zakończenie
koniec
Dziękuję Państwu za uwagę.
69/69


Wyszukiwarka