UNIWERSYTET TECHNOLOGICZNO-PRZYRODNICZY
w Bydgoszczy
ZAKŁAD ELEKTROENERGETYKI
LABORATORIUM INFORMATYKI
INSTRUKCJA DO ĆWICZENIA X
Pliki tekstowe i binarne
Opracował: dr inż. Marcin Drechny
Luty 2010 r.
2
1.
Celem ćwiczenia jest:
Celem tego ćwiczenia jest poznanie i utrwalenie podstawowych operacji związanych
z trwałym zapisywaniem i odczytywaniem danych programowych znajdujących się
w plikach tekstowych i binarnych przy podejściu tradycyjnym i obiektowym.
2.
Wprowadzenie
Jedną z podstawowych i najważniejszych umiejętności programistycznych jest
zarządzanie zasobami plikowymi. Pliki są podstawowymi zdefiniowanymi „kontenerami”
danych, trwale zapisanymi w zasobach dyskowych komputera.
Pliki możemy podzielić następująco:
a) pod względem funkcjonalnym na
- pliki tekstowe (zawierające podstawowe znaki ASCII np. znaki alfabetu),
- pliki binarne (zawierające bajty o kodzie od 0 do 255) np. pliki wykonywalne
z rozszerzeniem *.exe, *.com.
b) pod względem struktury wewnętrznej na
- pliki sekwencyjne,
- pliki o dostępie swobodnym ( pliki baz danych ).
Podstawowymi operacjami niezbędnymi do obsługi plików są:
- operacja otwarcia pliku,
- operacja zapisu do pliku,
- operacja odczytu z pliku,
- operacja zamknięcia pliku,
- operacje dyskowego zarządzania plikiem (kopiowanie, usuwanie
przemieszczanie pliku, oraz tworzenie nowego pliku).
Kolejność operacji wykonywanych na pliku przedstawia rysunek 1.
Znajomość
powyższych
zagadnień
umożliwia
realizowanie
podstawowego
celu
w programowaniu, a mianowicie – przetwarzanie informacji lub danych.
3
Wszystkie funkcje pozwalające na wykonanie operacji na plikach znajdują się w
standardowej wewnętrznej bibliotece C/C++. Ich prototypy określone sa w zbiorze
nagłówkowym stdio.h
Podstawowymi operacjami niezbędnymi do obsługi plików są:
• operacja otwarcia pliku,
• operacja zapisu do pliku,
• operacja odczytu z pliku,
• operacja zamknięcia pliku,
• operacje dyskowego zarządzania plikiem (kopiowanie, usuwanie przemieszczanie pliku,
oraz tworzenie nowego pliku).
2.1 Otwarcie pliku
FILE *
fopen
(const char *patch, const char *mode);
Funkcja fopen otwiera plik o nazwie i ścieżce dostępu określonej parametrem path i wiąże z
tym plikiem strumień,. Parametr mode służy do określenia sposobu, celu otwarcia oraz trybu
pracy strumienia. Ogólny format tego parametru przedstawia się następująco:
sposób-i-cel-otwarcia[tryb]
Gdzie sposób i cel otwarcia podane są w poniższej tabeli natomiast tryb pracy określany jest
literką:
Otwarcie pliku
Operacje odczytu
z pliku
Operacje zapisu
do pliku
Zamknięcie pliku
DYSK
PRZETWARZANIE INFORMACJI
Dane wejściowe
Dane wyjściowe
Fizyczna lokalizacja
pliku
Rys. 1 Miejsce pliku w systemie przetwarzania informacji
4
‘b’ dla pliku binarnego,
‘t’ dla pliku tekstowego brak trybu oznacza plik tekstowy
wt
otwarcie pliku do zapisu
wt+
otwarcie pliku do zapisu i odczytu
rt
otwarcie pliku do odczytu
rt+
otwarcie pliku do odczytu i zapisu
wt+ różni się od rt+ tym, że w pierwszym przypadku mamy możliwość zapisu, jak i zarówno odczytu, ale
zostanie utworzony nowy plik, natomiast w drugim wypadku plik nie zostanie utworzony, tylko zostanie otwarty
do odczytu i zapisu już istniejący plik. Jeżeli chcemy otworzyć już istniejący plik (chociaż taki naprawdę nie
znajduje się na dysku) zmienna plik przyjmie wartość NULL. Możemy więc w ten sposób sprawdzić, czy udało
się otworzyć plik, czy też nie:
Przykład 1:
plik=fopen("plik.txt","rt");
if
( plik==NULL ) printf("Nie można otworzyc pliku!");
Listing 1: otwarcie pliku
Przykład 2:
FILE *plik;
char
bufor[50];
5
plik=fopen("plik.txt","rt");
if
( plik==NULL ) { printf("Nie moge otworzyc pliku!");
return
0;}
fgets(bufor, 50, plik);
printf("%s",bufor);
fclose(plik);
2.2 Zapis do pliku
Po otwarciu pliku możemy dokonywać zapisu następującymi funkcjami:
fwrite
( const void *co,int rozmiar, int n, FILE *plik )
Wskazana funkcja wysyła do pliku „n” elementów o rozmiarze „ rozmiar” bajtów
pobranych z pliku wskazanego parametrem „plik”. W przypadku powodzenia operacji
wartością funkcji staje się liczba wszystkich wysłanych elementów (nie bajtów), a w
przypadku błędu wartość zero.
int
fputc
( int c, FILE *plik)
Funkcja wysyła znak określony parametrem c do pliku wskazanego parametrem plik.
Wartością funkcji jest wysłany znak, w przypadku błędu zaś EOF.
int
fputs
( const char *s, FILE *plik)
Funkcja wysyła łańcuch wskazany parametrem s do pliku wskazanego parametrem plik.
Funkcja nie dołącza znaku końca
wiersza (‘\n’), natomiast znak końca łańcucha (‘\0’) nie
zostaje wysłany. W przypadku powodzenia operacji wartość funkcji jest nieujemna, w
przeciwnym razie EOF.
Inną funkcją do zapisu w plikach tekstowych jest punkcja
fprintf
(FILE *plik,const char *format, ...)
Parametry formatowania takie same jak w printf()
Listing 2: Zapis struktury do pliku
6
2.3 Odczyt z pliku
Po otwarciu pliku możemy dokonywać odczytu z pliku następującymi funkcjami:
int
fread
(void *dokad, int rozmiar, int n, FILE *plik )
7
Wskazana funkcja odczytuje z pliku „n” elementów danych o rozmiarze „rozmiar” bajtów i
zapamiętuje odczytane dane w bloku pamięci wskazanych parametrem „dokad”. Wartością
funkcji w przypadku powodzenia operacji lub osiągnięcia końca zbioru jest liczba
rzeczywiście odczytanych elementów.
int
fgetc
( FILE *plik)
Funkcja ta odczytuje następny znak z pliku. Jeśli operacja powiodła się wartością funkcji jest
odczytany znak (po konwersji do typu int) W przypadku osiągnięcia końca zbioru wartością
funkcji jest EOF.
char *
fgets
( char *s, FILE *plik)
Funkcja ta odczytuje znaki z pliku wskazanego parametrem plik i umieszcza je w łańcuchu
s
. Odczyt jest przerywany po odczytaniu n-1 znaków lub końca wiersza, Funkcja umieszcza
na końcu łańcucha s znak końca wiersza oraz znak ’\o’. Oznaczający koniec łańcucha.
Wartością funkcji jest jest łańcuch s, a w przypadku błędu – adres pusty NULL.
Inną funkcją wykorzystywaną głównie w plikach tekstowych jest fscanf()
Listing
3: Odczyts struktury z pliku
8
2.4 Sterowanie wskaźnikiem pozycji w pliku
int
fseek
( FILE *plik, long przesuniecie, int pozycja)
Funkcja zmienia wartość wskaźnika pozycji w pliku na oddaloną o „przesuniecie” bajtów od
pozycji określonej parametrem „pozycja” (poniższa tabela)
STAŁA
WARTOŚĆ
POZYCJA W PLIKU
SEEK_SET
0
początek
SEEK_CUR
1
aktualna
SEEK_END
2
koniec
Wartość parametru „przesunięcie” oznaczającego przesunięcie względem pozycji, może być
zarówno ujemna (przesuniecie wstecz) jak i dodatnia (przesuniecie w przód).
Wartością funkcji staje się zero, jeśli przesunięcie wskaźnika powiodło się, lub wartość różna
od zera w przypadku błędu.
Listing 4: Funkcja przesunięcia wskaźnika w pliku
long
ftell
( FILE *plik)
Wartością powyższej funkcji jest aktualny wskaźnik pozycji w pliku Wartość ta jest mierzona
w bajtach od początku pliku (jeśli plik jest binarny)
void
rewind
( FILE *plik)
Funkcja ta przesuwa wskaźnik na początek pliku co jest równoważne z
fseek
( f1, 0L, SEEK_SET)
Listing 5: Przykład zapisu do pliku
9
Listing 6: Przykład odczytu z pliku
2.5 Podejście obiektowe do operacji na plikach
Podejście obiektowe do operacji na plikach zostało zaimplementowane dopiero w C++.
Korzysta się tu z standardowej klasy zdefiniowanej w pliku nagłówkowym fstream.h. Klasa
ta została wyprowadzona z innej klasy istream i reprezentuje strumień wejściowy znajdujący
się w pliku dyskowym. Od swojego przodka różni się tylko funkcjami realizującymi otwarcie
pliku.
10
Otwarcie strumienia dla odczytu realizuje się w następujący sposób:
ifstream
nazwa_strumienia
(char *
nazwa_pliku_powiązanego
,int
tryb_otw=
ios
::)
Otwarcie strumienia dla zapisu realizuje się za pośrednictwem innej klasy „ofstream”
(zdefiniowanej w fstream.h) wyprowadzonej z klasy nadrzędnej ostream. Składowe tej klasy
zapewniają otwarcie zbioru i umieszczenia w nim strumieni danych. Stworzenie tej klasy
realizuje się w następujący sposób:
ofstream
nazwa_strumienia
(char *
nazwa_pliku_powiązanego
,int
tryb_otw=
ios
::)
Powyższa instrukcja stworzy strumień wyjściowy i otwiera dla niego plik o nazwie podanej w
pierwszym parametrze.
Tryby pracy strumienia określa się za pomocą stałych przedstawionych w poniższej tabeli:
Tryby otwarcia pliku
Stała
wartość
Znaczenie
in
0x01
Otwarcie do odczytu.
out
0x02
Otwarcie do zapisu.
ate
0x04
Przesuń wskaźnik pozycji na koniec pliku podczas otwierania.
app
0x08
Zapis danych na końcu zbioru (append).
trunc
0x10
Pozbądź się zawartości pliku, jeśli już istnieje.
nocreate
0x20
Próba otwarcia nie powiedzie się jeśli wskazany plik nie istnieje.
noreplace
0x40
Próba otwarcia nie powiedzie się jeśli wskazany plik istnieje.
binary
0x80
Tryb binarny pracy strumienia (normalnie tekstowy).
11
Listing 7: Przykład zapisu do pliku - podejście strumieniowe
Listing 8: Przykład odczytu z pliku - podejście strumieniowe
Wynik działania programu Plik.txt
12
13
3.
Przebieg ćwiczenia – zadania do wykonania
Pliki tekstowe
3.1 Napisać program do podziału (zadanego przez prowadzącego) pliku tekstowego na pliki
o mniejszym rozmiarze. Program ma dokonywać podziału na poziomie kolejnych linii
tekstowych (przy użyciu instrukcji fgetchar lub fgets) Stosunek podziału podaje
prowadzący ćwiczenia (np. podział 1 do 5 – zadany plik zostaje podzielony na 5
mniejszych).
Przed przystąpieniem do pisania programu narysować algorytm działania oraz
skonsultować go z prowadzącym.
3.2. Zrealizować osobny program, którego zadaniem będzie scalenie podzielonych plików.
Przed przystąpieniem do pisania kodu źródłowego opracować algorytm działania
programu.
3.2 Dana jest tablica char dana[100] = ”To jest linia 1\n To jest linia 2\n To jest linia 3\n”.
Wykorzystując funkcję fputc zapisać zawartość tablicy do pliku:
a) o nazwie dane_tt.dat w trybie otwarcia tekstowego (wt);
b) o nazwie dane_tb.dat w trybie otwarcia binarnego (wb).
Wykorzystując funkcję fgetc wczytać do bufora unsigned char buf[100] dane zapisane w
pliku dane_tt.dat w trybie otwarcia tekstowego (rt) oraz w trybie otwarcia binarnego (rb).
Dla każdego z trybów wyprowadzić na ekran zawartości buforów oraz liczby wczytanych
znaków. Powtórzyć operacje dla pliku dane_tb.dat oraz trybów (rt) i (rb). Wyciągnąć
wnioski dotyczące zapisu i odczytu danych w trybie tekstowym i binarnym.
3.3 Opracować następujące funkcje obsługi plików znakowych:
a) zapisującą znaki wprowadzane z klawiatury do pliku o ustalonej nazwie w trybie
otwarcia binarnego; znaki wprowadzać za pomocą funkcji fputc; wprowadzanie
znaków
zakończyć po napotkaniu ESC; prototyp: PiszZn(FILE *f, char *nazwa);
14
b) odczytującą zawartość pliku znak po znaku w trybie otwarcia binarnego i
wyprowadzającą znaki na ekran; wykorzystać funkcję fgetc; prototyp: CzytajZn(FILE
*f, char *nazwa);
c) wyznaczającą długość pliku w bajtach; wykorzystać funkcje fseek i ftell; prototyp:
long IleBP(FILE *f);
d) dopisującą znak na zadanej pozycji w pliku (znak powinien być wstawiony pomiędzy
istniejące znaki); prototyp: Dopisz(FILE *f; char *nazwa, long pozycja, int znak).
3.4 Wykorzystując funkcję fprintf zapisać do pliku dane typu int, char, float oraz daną
typu łańcuchowego w wybranym formacie (np. %5d%3c%8.2f%10s; dla danych 23, 'A',
234.57, "Ala_Ola" w pliku fizycznym zapisane zostaną ∇∇∇23∇∇A∇∇234.57∇∇∇
Ala_Ola). Odczytać wprowadzone dane za pomocą funkcji fscanf. Należy pamiętać, że
podczas odczytu, dla wszystkich formatów oprócz %c, przed zinterpretowaniem są
pomijane spacje. W celu pominięcia zbędnych spacji można wykorzystać format
%*[∇]%c.
3.5 Dana jest struktura typu struct tosoba { char naz[20]; unsigned wiek; long id; }.
Opracować
następujące funkcje:
a) zapisującą do pliku znakowego łańcuch ”-- ... --\n” złożony z 30 znaków ’-’ i jednego
znaku ’\n’, a następnie pola zmiennej strukturalnej tosoba w wybranym formacie
(np. dla fprintf: nazwisko - %20s\n, wiek - %6u\n, identyfikator - %10ld\n);
b) odczytującą z pliku łańcuch złożony z 30 znaków ’-’ i jednego znaku ’\n’, a następnie
pola struktury typu tosoba rozdzielone znakami przejścia do nowej linii (pominięcie
sekwencji ”-- ...--\n” o dowolnej długości można zrealizować za pomocą formatu
%*[-\n], który oznacza, że znaki ’-’ oraz ’\n’ są pomijane; czytanie rozpocznie się jeśli
pojawi się znak różny od [-\n]).
W programie głównym zapisać do pliku N struktur (N – stała), a następnie odczytać dane.
3.6 W pliku tekstowym p1.txt znajduje się K liczb całkowitych rozdzielonych spacjami i
posortowanych rosnąco. W pliku tekstowym p2.txt znajduje się N liczb całkowitych
15
rozdzielonych spacjami i posortowanych rosnąco. Opracować funkcję, która realizuje
algorytm sortowania przez łączenie:
a) otwiera pliki p1.dat i p2.dat;
b) odczytuje pierwszą liczbę z pliku p1 i pliku p2;
c) porównuje odczytane liczby i mniejszą z nich zapisuje do pliku p3.dat;
d) powtarza operacje odczytu i porównywania danych, aż do wyczerpania
danych w obu plikach.
Na przykład dla plików p1 i p2, zawierających elementy 2, 4, 5, 7 (plik p1)
oraz 3, 6, 9 (plik p2), przedstawiona procedura tworzy plik p3, który zawiera
elementy 2, 3, 4, 5, 6, 7, 9.
3.7 Dany jest plik tekstowy zawierający liczby rzeczywiste typu double zapisane w formacie
z dwoma miejscami po przecinku i rozdzielone spacjami. Napisać program, który:
• zliczy liczbę danych w pliku, czytając dane z pliku aż do napotkania końca pliku lub
do pierwszego nieudanego odczytu;
• przewinie plik na początek;
• dokona dynamicznej alokacji tablicy typu double, która pomieści wszystkie dane z
pliku;
• wczyta dane z pliku do utworzonej tablicy;
• zamknie plik;
• wyprowadzi dane z tablicy na ekran.
Przed zakończeniem programu zwolnić zaalokowaną pamięć.
Pliki binarne
3.8 Wykorzystując funkcję fwrite zapisać do pliku binarnego daną typu char, int, long,
double oraz tablicę znaków. Odczytać dane z pliku za pomocą funkcji fread.
3.9 Zdefiniować typ tdana, np. typedef double tdana. Opracować program, który zapisze do
pliku binarnego dane w formacie: rozmiar danej typu tdana (zmienna typu unsigned),
liczba
danych w pliku (zmienna typu long), ciąg danych typu tdana zapisywanych do pliku, a
następnie odczyta dane z pliku i wyprowadzi na ekran.
16
3.10 Opracować program, który na podstawie rozmiaru pliku binarnego określi ile zawiera
on liczb typu long, a następnie utworzy dynamiczną tablicę liczb typu long i wczyta do
niej zawartość pliku. Wyprowadzić zawartość tablicy na ekran. Przed zakończeniem
programu zwolnić przydzieloną pamięć. Uwaga: rozmiar pliku można określić za
pomocą funkcji ftell.
3.11 Zdefiniować typ strukturalny typedef struct tosoba { char naz[20]; long id; }.
Opracować następujące funkcje obsługujące plik złożony ze zmiennych strukturalnych
typu tosoba:
a) inicjującą zmienną typu tosoba losowymi wartościami;
prototyp: void Inicjuj(tosoba& r);
b) wyprowadzającą zawartość zmiennej typu tosoba na ekran;
c) wyprowadzającą zawartość zmiennej typu tosoba na ekran;
d) dopisującą zmienną typu tosoba na końcu pliku o podanej nazwie;
prototyp: void Fdopisz(FILE *f, char *nazwa, const tosoba *r);
e) odczytującą zmienną z zadanej pozycji w pliku o podanej nazwie i zapisującą
daną do r;
prototyp: void Fczytaj (FILE *f, char *nazwa, long poz, tosoba& r);
f) wstawiającą, pomiędzy istniejące dane, nową daną na zadanej pozycji w pliku o
podanej nazwie; prototyp: void Fwstaw (FILE *f, char *nazwa, long poz, const
tosoba *r);
g) usuwającą element z zadanej pozycji w pliku i zapisującą daną do r;
prototyp: void Fusun (FILE *f, char *nazwa, long poz, tosoba& r).
h) Przetestować opracowane funkcje w programie.
3.12 Opracować program, który umożliwia przeglądanie zawartości dowolnego pliku o
podanej nazwie. Zawartość pliku wyprowadzać wierszami po 16 bajtów w postaci
szesnastkowej
i obok te same dane w postaci znakowej. W przypadku napotkania znaku sterującego
(np. ’\n’, znaki o kodach ASCII mniejszych niż 32) wyprowadzić kropkę.
17
4.
Proponowana literatura:
- Harvey M. Dietel, Paul J. Dietel, Arkana C++: programowanie, Wydawnictwo RM,
Warszawa, 1998,
- Nabajyoti Bakarati, Biblia Turbo C++, LT & P, Warszawa, 1993,
- P. J. Plauger, Biblioteka standardowa C++, WNT, Warszawa, 1997,
- Namir C. Shammas, Borland C++ 4 kurs na zderzenie: [najszybszy sposób nauki
programowania !]
, Intersoftland, Warszawa, 1995,
- Piotr Metzger, C++: pierwsze kroki, Helion, Gliwice, 1994,
- Jesse Liberty, C++: ksiega eksperta, Helion, Gliwice 1999,
- Tony L. Hansen, C++: zadania i odpowiedzi, WNT, Warszwa, 1994,
- Davis Stephen R., C++ dla opornych, Oficyna wydawnicza Read Me, Warszawa, 1995,
- Paul Snaith, C++ nie tylko dla orłów, Intersoftland, Warszawa, 2000,
- Adam Majczak, C++ w 48 godzin, Intersoftland, Warszawa, 1993,
- Tomasz Kopacz, Język C w praktyce: Turbo C i C++, Zakład Nauczania Informatyki
Mikom, Warszawa, 1993,
- Kisielewicz Jerzy, Język C w środowisku Borland C++, Oficyna wydawnicza Politechniki
Wrocławskiej, Wrocław, 1998,
- David Vandevoorde, Język C++; ćwiczenia i rozwiązania, WNT, Warszawa, 2001,
- Bjarne Stroustrup, Język C++, WNT, Warszawa 1994, 1995, 2000,
- Alan Neibauer, Języki C i C++: twój pierwszy program, Komputerowa Oficyna
Wydawnicza Help, Warszawa, 1995,
- Clovis L. Tondo, Bruce P. Lenug, Podstawy języka C++: ćwiczenia i rozwiązania, WNT,
Warszawa, 2001,
- Stanley B. Lippman, Podstawy języka C++, WNT, Warszawa, 1994, 1996, 2001,
- Adam Majczak, Praktyczne programowanie w C++, Intersoftland, Warszawa, 1996,
- Steven Holzner, Programowanie w Borland C++, Intersoftland, Warszawa, 1993,
- Andrzej Zalewski, Programowanie w językach C i C++ z wykorzystaniem pakietu
Borland C++, Wydawnictwo Nakom, Poznań, 1994, 1997, 1998,
- Paweł Chomicz, Robert Ulijasz, Programowanie w jezyku C i C++, Wydawnictwo PLJ,
Warszawa, 1992,
- Kris Jamsa, Wygraj z C++, Zakład Nauczania Informatyki Mikom, Warszawa, 1996,
- Conor Sexton, Język C++ to proste, Wydawnictwo RM, Warszawa, 2001,
18
- Alan R. Neibauer, Języki C i C++. Twój pierwszy program, Komputerowa Oficyna
Wydawnicza HELP, Warszawa, 1995,
- Wiesław Porębski, Programowanie w języku C++, Komputerowa Oficyna Wydawnicza
HELP, Warszawa, 2001,
- Adam Majczak, C++ dla licealistów i studentów, Wydawnictwo Translator S.C. ,
Warszawa, 2003.