Pascal posiada kilka typów plików:
pliki tekstowe(można je podglądać w zwykłym notatniku)
pliki znakowe(bardzo podobne do powyższego typu)
pliki liczbowe(nie można ich podglądać w notatniku)
pliki rekordowe(nie można ich podglądać w notatniku)
Zmienne plikowe definiuje się za pomocą typów plikowych, np.:
type plik_znk = file of char; plik_licz = file of byte; var znaki: plik_znk; liczby: plik_licz;
Tak zadeklarowane zmienne: plik_znk i plik_licz trzeba jeszcze wykonać dwie rzeczy: związać zmienne z plikami dyskowymi oraz ustawić tryb pracy.
Zwiążmy zmienne z plikami - służy do tego instrukcja ASSIGN, jej składnia wygląda tak:
assign(znaki,'znaki.dat'); assign(liczby,'liczby.dat');
I jeszcze tylko ustawmy tryb pracy i będziemy gotowi do pracy!
Są trzy tryby pracy(alfabetycznie):
append(zmienna_plikowa) - umożliwia dopisywanie nowych danych do pliku(plik musi istnieć - tylko dla plików TEKSTOWYCH)
reset(zmienna_plikowa) - otwiera plik do czytania(plik musi istnieć)
rewrite(zmienna_plikowa) - otwiera plik do pisania, usuwa całą zawartość i zaczyna zapisywanie od początku pliku, jeśli plik nie istnieje - tworzy go.
Oczywiście w jednym programie, funkcji czy procedurze możemy najpierw coś dopisać, potem odczytać - trzeba tylko pamiętać o zmianie trybów, nic bowiem nie dopiszemy jeśli plik jest w trybie reset - czytania, do póki nie przejdziemy do trybu append.
Odczyt i zapis do pliku.
Realizuje się go za pomocą standardowych instrukcji read i write. Jako pierwszy argument występuje zmienna plikowa, drugim argumentem jest zmienna typu takiego jako sam plik. Czyli w naszym przykładzie plik_znk i char oraz plik_licz i integer. Np.:
read(znaki,q); { var q: char; }
read(liczby,w); { var w: interger; } write(znaki,q); {
var q: char; } write(liczby,w); { var w: interger; }
Wszelkie dokonywane zmiany w trakcie działania programu nie są dokonywane na bieżąco - dopiero wywołanie instrukcji close - powoduje zapisanie pliku na dysk.
Close(zmienna_plikowa);
Warto jeszcze przedstawić przykład kodu, który sprawdza czy plik do odczytu istnieje.
{-} repeat write('Podaj pelna nazwe
pliku do otwracia: '); readln(nazwa);
assign(zmienna_plikowa,nazwa); reset(zmienna_plikowa); if IOresult <> 0 then
writeln('Podany plik nie istnieje, wprowadz ponownie!'); until IOresult = 0; {+}
Pliki tekstowe.
Do plików tekstowych możemy wpisywać zarówno znaki jak i liczby, możemy dodawać nowe linie, kasować całe linie, dopisywać do istniejącego pliku etc. Pliki tekstowe inaczej też się deklaruje.
type plik_txt = text;
Pliki tekstowe dzielą się na wiersze, każdy z nich kończy się sekwencją EOLN - znakami: CR(powrót karetki) oraz LF(przejście do nowej linii).
Cały plik z kolei kończy się sekwencją EOF - znakami: [CTRL] [Z].
Stwórzmy więc plik tekstowy i wpiszmy do niego kilka linii tekstu.
program pliki_tekstowe; var plik: text; nazwa,linia: string[80]; { wspolrzedne X ekranu maja 80, latwiej bedzie
nam pozniej wyswietlac zawartosc pliku, jesli plik bedzie mial taka dlugosc
wierszy } begin writeln('Podaj nazwe pliku, ktory mam
utworzyc: '); readln(nazwa); assign(plik,nazwa); rewrite(plik); writeln('OK -
plik juz utworzony teraz wprowadz kilka linii tekstu'); writeln('linia
zawierajaca sama kropke - ''.'' koczy wpisywanie'); while linia <> '.' do begin readln(linia); if linia
<> '.' then writeln(plik,linia); {zauwaz, ze w
plikach nietekstowych nie mozna uzywac writeln} end;
close(plik); writeln('Dziekuje dane zostaly zapisane do
pliku: ',nazwa); readln; end.
Zawartość plików tekstowych można wyświetlać na różny sposób. Pierwszym jest odczyt znak po znaku:
program pliki_tekstowe; procedure pokaz(nazwa: string);
var plik: text; c: char; begin
assign(plik,nazwa); reset(plik); if eof(plik) then writeln('Plik ', nazwa, ' jest pusty') else begin repeat read(plik,c); write(c); until
eof(plik); end; end; var nazwa: string; begin write('Podaj nazwe pliku, ktorego zawartosc mam
wczytac: '); readln(nazwa); writeln('Zawartosc pliku ',nazwa,':'); pokaz(nazwa);
writeln('Koniec pliku.'); readln; end.
Drugim sposobem jest korzystanie wprost z funkcji readln.
Poniższy program oferuje możliwość przeglądania plików tekstowych, które mają więcej wierszy niż można wyświetlić na ekranie, zawartość pliku umieszczona jest w ramce.
program pliki_tekstowe; uses crt; var nazwa: string; i: byte; procedure
pisz(nazwa: string); var plik:
text; linia: string[77]; { uwaga inna dlugosc linii -
juz nie 80 - musze miec miejsce na ramke } i: byte; begin assign(plik,nazwa); reset(plik); { nie sprawdzam czy
dany plik istnieje - zakladam, ze tak } while not eof(plik) do begin for i:=0 to 21 do begin readln(plik,linia); { rysuje zawartosc w ramce }
writeln(chr(186):1, linia:-1, chr(186):78-length(linia)); { wychodz z pliku, nie
czekaj do i = 24, juz jest koniec } if eof(plik) then exit; end; if not
eof(plik) then begin
textcolor(lightred); write(chr(186),' >> '); write('Nacisnij enter aby
przewinac'); write(' << ',chr(186)); readln; textcolor(7); end; end; close(plik); end; begin write('Podaj nazwe pliku,
ktorego zawartosc mam wczytac: '); readln(nazwa); writeln('Zawartosc pliku
',nazwa,':'); write(chr(201)); for i:=0 to 76 do write(chr(205));
writeln(chr(187)); pisz(nazwa); write(chr(200)); for
i:=0 to 76 do write(chr(205));
writeln(chr(188)); writeln('Koniec pliku.'); readln; end.
Możemy również stworzyć własny typ pliku tekstowo-rekordowego. Jeśli rekordy będą przechowywane w pliku tekstowym, wtedy trzeba będzie każde pole zapisywać oddzielnie, rekord będzie się kończył wraz z nową linią. Krórki zarys problemu:
program pliki_tekstowe; uses ciagi; type licznik = record dzial : string[21]; autor :
string[21]; ile : longint; end; var plik: text; stats: licznik;
nazwa: string; rozmiar: integer; { rozmiar musi byc
znany i rowny rozmiarom rekordow } begin rozmiar:=21; {
ciagi autor i dzial moga miec maksymalnie 21 znakow }
assign(plik,'rekordy.txt'); rewrite(plik); with stats
do begin autor:='Lukasz
Budnik'; dzial:='Kurs programowania TP'; ile:=1222; {zapisujemy do pliku, zmienne oddziel spacja}
writeln(plik,autor:rozmiar,dzial:rozmiar,ile); { mozemy dokonac zmian }
autor:='q'; dzial:='qwq'; ile:=0; { a potem je odtworzyc oryginalne wartosci z
pliku i wyswietlic je } reset(plik); read(plik,autor,dzial,ile); { usuwamy puste
spacje z lewej, jest ich zawsze: rozmiar - length(ciag) uzywamy funkcji ltrim z
naszego modulu Ciagi } autor:=ltrim(autor); dzial:=ltrim(dzial); { tego problemu
nie ma z liczbami } writeln(autor); writeln(dzial); writeln(ile); close(plik);
end; { sekcji with } readln;
end.
Pliki znakowe.
Rozmiar plików znakowych wynosi dokładnie tyle bajtów ile jest w nim zapisanych znaków ASCII.
Poniżej prezentuję krótki program tworzący plik znaki.dat, wpisujący do niego znaki 'a'..'z', potem przepisuje całą zawartość pliku znaki.dat do pliku znaki2.dat zmieniając przy okazji wszystkie litery na ich wielkie odpowiedniki.
program pliki; type plik_znk = file of char; var znaki, znaki2:
plik_znk; i: byte; q: char; begin { przydzielmy zmienne
do konkretnych plikow } assign(znaki,'znaki.dat'); {
mozna podawac pelna sciezke } rewrite(znaki); for i:=0
to 25 do begin q:=chr(i+97); write(znaki, q); end; close(znaki); assign(znaki2,'znaki2.dat');
rewrite(znaki2); reset(znaki); { odczyt pliku znaki} while not eof(znaki) do begin read(znaki,q); { pobieram
znak - typ char } q:=upcase(q); { mala -> wlk litera } write(znaki2,q); {
zapis wlk litery do nowego pliku } end; close(znaki2); { zapisz zmiany, plik znaki zostal juz
wczesniej zapisany, teraz tylko z niego czytalem } readln; end.
Pliki liczbowe
Rozmiar plików znakowych wynosi: rozmiar bajtów typu * ilość zapisanych liczb.
Poniżej prezentuję krótki program tworzący plik liczby.dat, wpisujący do niego liczby typu longint od 1..10, potem zamknę plik i otworzę go ponownie na dopisywanie i dopiszę jeszcze 10 kolejnych liczb. Jak wiemy nie możemy skorzystać z instrukcji APPEND bo działa ona tylko na plikach tekstowych, trzeba więc zapisać poprzednią zawartość pliku i zapisać ją ponownie, tym razem powiększoną o 10 elementów.
program pliki; type plik_licz = file of longint; var liczby: plik_licz;
i: byte; z: longint; tab: ARRAY[1..10] of longint; begin { przydzielmy
zmienne do konkretnych plikow }
assign(liczby,'liczby.dat'); rewrite(liczby); randomize; z:=1000+random(10000);
{ losujemy jakas liczbe wieksza od 1 tys } for i:=1
to 10 do begin write(liczby, z); inc(z); end;
close(liczby); { zamykam plik } { otworzmy plik, zapiszmy stare liczby w
pomocniczej tablicy } reset(liczby); for i:=1 to 10 do begin read(liczby,tab [i]); end; {
zapiszmy wiec ponownie plik, najpier zapiszmy stare liczby, odtworzmy je z
tablicy } rewrite(liczby); for i:=1 to 10 do begin write(liczby, tab [i]); end; {
i dopiero teraz mozemy dopisac nowe liczby - droga przez meke, ale inaczej sie
nie da ;D } z:=random(100); for i:=1 to 10 do begin write(liczby, z); inc(z); end;
close(liczby); { zamykam plik } readln; end.
Pliki rekordowe
Tym razem już sam przykład.
program pliki; type { definicja rekordu } licznik = record dzial : string[20]; autor :
string[20]; ile : longint; end; plik_rek = file of licznik; var rekordy: plik_rek;
stats: licznik; i: byte; z: byte; q: char; begin
assign(rekordy,'rekordy.dat'); rewrite(rekordy); with
stats do begin autor:='Lukasz
Budnik'; dzial:='Kurs programowania TP'; ile:=1222; end; write(rekordy,stats); { wpisujemy caly rekord! nie
trzeba podawac kazdego pola osobno! } close(rekordy); { mozemy dokonac zmian }
stats.autor:='q'; stats.dzial:='qwq'; stats.ile:=0; { a potem je odtworzyc
oryginalne wartosci z pliku i wyswietlic je } reset(rekordy);
read(rekordy,stats); writeln(stats.autor,' ', stats.dzial,' ',stats.ile);
close(rekordy); readln; end.
To dane, które mogą być wieloczłonowe, np.: zrobiłeś jakąś gierkę to możesz wstawić do niej dane jako rekord, który będzie składał się z daty, ksywy zawodnika, liczby punktów, liczby zdobytych bonusów etc. Wszystko to można umieścić w jednej zmiennej. Na tej samej zasadzie pracują programy np.: w bibliotekach. Mamy: Tytuł książki, Autora, kto ją teraz ma, od kiedy ją ma etc. Przykład deklaracji rekordu dla biblioteki:
TYPE InfoBibI = RECORD { ponizej podaje pola rekordu i ich typ } Tytul,Autor:
string[1..30]; Cena: Real ; IlEgz: 0..100 ; Status:
Char END ; VAR Book : InfoBibl
;
Do pól rekordu Book możemy wpisywać wartości instrukcjami przypisania:
Book.Tytuł ;= 'Organizacja maszyn cyfrowych' ; Book.Autor :=
'Yaochan Chu'; Book.Cena ;= 150.0; Book.IlEgz := 10; Book.Status:= 'C' ;
Tablice rekordów możemy kopiować wprost:
Book2 := Book; Book3 := Book;
Proste, ale prawdziwe wtedy i tylko wtedy, gdy Book, Book2 i Book3 są tego samego typu czyli: InfoBibl.
Sprawa się troszku komplikuje(ale tylko troszkę), gdy mamy dwa różne rekordy i nazwy pól rekordów są inne(zakładam, że typy pól są takie same), wtedy trzeba ręcznie kopiować pole do pola. W poniższym przykładzie mamy dwa rekordy, są one niemal identyczne, tylko inaczej nazywają się ich pola.
TYPE Info1 = Record wiek : 0..120 ; p : char end;
Info2 = Record lata: 0..120 ; sex: char end; VAR Pierwszy : Info1 ; Drugi :
Info2; Pierwszy.wiek : = Drugi.Lata ; Drugi.sex := Pierwszy.p ;
A oto przykład.
program pokaz_rekordow; uses crt; type licznik = record dzial : string[20]; autor :
string[20]; ile : longint; end; var stats : licznik; procedure pobierz; begin
writeln('Podaj dzial, pole moze zawierac 20 liter: '); readln(stats.dzial);
writeln('Podaj autora, pole moze zawierac 20 liter: '); readln(stats.autor);
writeln('Podaj ilosc wywolan, maks. longint: '); readln(stats.ile); end; procedure pokaz; begin write('Dzial: '); writeln(stats.dzial); write('Autor:
'); writeln(stats.autor); write('Ilosc wywolan: '); writeln(stats.ile); end; begin clrscr; pobierz; pokaz;
readln; end.
Idąc za ciosem przeróbmy powyższy program na bazę danych, w tym celu utworzę tablicę rekordów:
program prosta_baza; uses crt; type licznik = record dzial : string[20]; autor :
string[20]; ile : longint; end; { wszystko globalnie } const
MAX=3; var stats : ARRAY[1..MAX] of licznik; i:
integer; procedure pobierz; begin writeln('+----------------------------+'); for i:=1 to MAX do begin writeln('REKORD nr: ',i);
writeln('Podaj dzial, pole moze zawierac 20 liter: '); readln(stats [i].dzial);
writeln('Podaj autora, pole moze zawierac 20 liter: '); readln(stats [i].autor);
writeln('Podaj ilosc wywolan, maks. longint: '); readln(stats [i].ile); writeln;
end; end; procedure pokaz; begin
writeln('+----------------------------+'); for i:=1
to MAX do begin writeln('REKORD nr: ',i); write('Dzial: ');
writeln(stats [i].dzial); write('Autor: '); writeln(stats [i].autor);
write('Ilosc wywolan: '); writeln(stats [i].ile); writeln; end; end; begin clrscr; pobierz; pokaz; readln; end.
Instrukcja wiążąca WITH
WITH jest instrukcją dzięki której oszczędzimy trochę na pisaniu w kółko nazwy rekordu. Załóżmy, że dokonujemy wpisu do zmiennej Book będącej rekordem typu BiblInfo. Najprościej jest zrobić:
book.tytul:='qwq'; book.autor:='wqq'
Czy na pewno? Za każdym razem podajemy "book" - nie że jesteśmy leniwi, ale możemy związać wczytywane zmienne z polami rekordów. Np.:
with book do
begin tytul:='qwq; autor:='wqq'; end; { dalej juz zmienne nie beda wiazane z rekordem book }
Rekordy z wariantami.
Są to tak zwane zmienne rekordowe. Rekordach z wariantami używane są jeśli piszemy uniwersalne programy, np.: operujemy na stopach i metrach jednocześnie i to od użytkownika będzie zależało czy chce posługiwać się takim a nie innym systemem jednostkowym.
type miary = record case rodzaj: (ang,pol) of ang: (stopy,cale: integer); pol: (metry,centym: integer)
end;
Teraz pokażę jak wykorzystać to w programie. Program nie jest ambitny, ale przynajmniej pokazuje możliwości i zalety wariantów.
program warianty; uses crt; type miary = record case rodzaj: (ang,pol) of ang: (stopy,cale: integer); pol: (metry,centym: integer)
end; var wymiary: miary;
wybor: char; procedure pobierz; begin if wymiary.rodzaj = pol then begin writeln('Podaj po spacji
metry i centymetry: '); with wymiary do begin readln(metry,centym); end; end else begin writeln('Podaj po spacji
stopy i cale: '); with wymiary do begin readln(stopy,cale); end; end; end; {procedury} begin clrscr;
wymiary.rodzaj:=pol; writeln('Domyslnym systemem miar jest polski system -
metrowy, wpisz ''Z'', aby zmienic'); readln(wybor); if
upcase(wybor) = 'Z' then begin
wymiary.rodzaj:=ang; { jeszcze nie wprowadzilem rekordow, nie musze nic
przeliczac } writeln('Zmieniono, teraz miary beda podawne w stopach'); end; writeln; pobierz; {tu mozesz umiescic jakies dalsze
instrukcje, to tylko przyklad} readln; end.