Korzystanie z plików i rekordów w programach
Kolejnymi strukturami danych są pliki. Plik jest ciągiem elementów tego samego typu (np. znaków, liczb), przy czym liczba jego składowych może się zmieniać. Jest on logicznym odpowiednikiem fizycznego pliku danych, znajdującego się poza programem. W danej chwili może być dostępna tylko jedna składowa pliku.
Pliki stużą do komunikacji użytkownika z komputerem - zapisujemy w nich nie tylko programy, ale także dane do programów i wyniki wykonywanych obliczeń.
W języku Pascal plikiem nazywamy zarówno strukturę danych, jak i zmienną plikową. Pliki przetwarzamy według następującego schematu.
1. Deklarujemy zmienną plikową, zwaną plikiem.
2. Kojarzymy plik z plikiem fizycznym, np. znajdującym się na dysku.
3. Otwieramy plik, np. do odczytu lub do zapisu.
4. Wykonujemy operacje na elementach pliku.
5. Zamykamy plik.
6. Jeśli był to roboczy plik programu, to usuwamy plik fizyczny.
Operacje te wykonuje się na plikach, korzystając ze standardowych procedur i funkcji, dla których przyjmujemy, że p oznacza plik, czyli zmienną plikową.
• Procedura Assign(p,'nazwaPlikuFizycznego') kojarzy plik p z plikiem fizycznym, np. Assign(p,'A:\Dane.txt'). Zmiennej plikowej nie można przypisać pliku instrukcją przypisania.
• Procedura Reset(p) otwiera istniejący plik p do odczytu od początku; instrukcję tę można wykonywać po wywołaniu procedury Assign.
• Procedura Rewrite (p) tworzy i otwiera plik p do zapisu; instrukcję tę można wykonywać po wywołaniu procedury Assign.
• Procedura Close(p) zamyka plik p.
• Procedura Erase(p) usuwa plik fizyczny skojarzony z plikiem p; plik p nie może być otwarty. Jeśli w programie tworzy się pliki robocze, to należy je usuwać.
• Funkcja Eof (p) ma wartość True, jeśli został osiągnięty (np. podczas czytania) koniec pliku p, a False - w przeciwnym razie.
Elementy pliku mogą być czytane z pliku (np. przez procedurę Read) lub zapisywane do pliku (np. przez procedurę Write).
Poznamy dwa rodzaje plików: tekstowe i jednorodne.
Pliki tekstowe
Plik tekstowy składa się ze znaków połączonych w wiersze. Każdy wiersz jest zakończony symbolem końca wiersza (złożonym z dwóch znaków #13=CR i #10=LF), a po ostatnim wierszu jest znak #26=AZ (CTRL+Z), oznaczający koniec pliku. Symbole końca wiersza są generowane klawiszem Enter lub za pomocą instrukcji Writeln.
Plik tekstowy określa się mianem Text. Jest to standardowa nazwa tekstowego typu plikowego. Plik tekstowy t deklarujemy więc jako var t:Text. Elementy tego pliku (znaki lub wiersze) można przetwarzać tylko sekwencyjnie, tzn. można czytać albo zapisywać bez cofania się. Plik tekstowy może być otwarty tylko do czytania albo tylko do pisania. Ponadto dla pliku tekstowego t są dostępne:
• procedura Append(t), otwierająca istniejący plik t do dopisywania na jego końcu;
• funkcja Eoln(t), która ma wartość True, jeśli został osiągnięty koniec wiersza w pliku t (lub koniec pliku t), a False w przeciwnym razie.
Komunikacja z urządzeniami zewnętrznymi (np. klawiaturą, ekranem, drukarką) odbywa się za pomocą plików tekstowych. Pliki te są używane m.in. do przechowywania danych dla programów i wyników ich działania. W przedstawionych dotychczas programach na ogół dane były podawane z klawiatury za pomocą instrukcji Read, a wyniki były wyświetlane na ekranie przy użyciu instrukcji Write. Dla ułatwienia posługiwania się plikami tekstowymi, w języku Pascal niejawnie skojarzono standardowe zmienne plikowe z urządzeniami (patrz tabela).
Urządzenia zewnętrzne i odpowiadające im zmienne plikowe
Urządzenie zewnętrzne |
Standardowa nazwa urządzenia |
Standardowa zmienna plikowa (typu Text) |
klawiatura |
CON |
Input |
Ekran |
CON |
Output |
drukarka |
LPT1 |
Lst (ze standardowego modułu Printer) |
Pliki reprezentowane przez standardowe zmienne plikowe są zawsze otwarte, a operacje na nich dotyczą odpowiednich urządzeń zewnętrznych. Dla tych plików nie wykonuje się procedur: Assign, Reset, Rewrite, Close, ponieważ są wykonywane automatycznie. Dzięki temu, np. instrukcja Read(a) dla a typu integer jest rozumiana jako „przeczytaj liczbę całkowitą z klawiatury i przypisz jej wartość zmiennej a". Podobnie np. instrukcja Write(a) oznacza „wyświetl na ekranie monitora wartość zmiennej a". W tej komunikacji pośredniczą niewidoczne dla użytkownika pliki Input i Output.
Podsumujmy: wprowadzanie danych do programu z klawiatury może być uciążliwe, jeśli ich liczba jest znaczna, np. w przypadku gdy chcielibyśmy wprowadzić 1000 liczb. Wyprowadzanie dużej liczby wyników na ekran też może nie być wygodne, gdy nie mieszczą się na ekranie. Najwygodniej byłoby czytać dane z pliku i wysyłać wyniki do innego pliku. Pliki te moglibyśmy dalej przetwarzać. Wszystko to jest możliwe, jeśli zastosujemy instrukcje czytania i wypisywania, w których pierwszym parametrem jest nazwa odpowiedniego pliku tekstowego, np. dane lub wyniki:
Read(dane,...) Write(wyniki,...)
Readln(dane,...) Writeln(wyniki,...)
Readln(dane) Writelnfwyniki)
Pliki te trzeba wcześniej zadeklarować jako zmienne plikowe var dane,wyniki:Text;
Ponadto z plikami dane i wyniki należy związać pliki fizyczne (tj. znajdujące się na dysku) i wykonać na nich operacje otwarcia:
Assign (dane,' DaneSort. txt'); Reset (dane);
i Assign(wyniki,'WynSort.txt'); Rewrite(wyniki);
a na końcu programu zamknąć je: Close(dane); Close(wyniki);
W języku Pascal nie można deklarować funkcji o wartości typu plikowego i dlatego w programach występują procedury. W procedurze każdy parametr typu plikowego, przeznaczony zarówno na wyniki, jak i na dane, musi być przekazywany przez zmienną, czyli poprzedzony słowem var.
W programie można sprawdzać, czy istnieje plik fizyczny, z którego chcemy czytać dane. Służy do tego dyrektywa kompilatora {$I+}, powodująca włączenie kontroli każdej operacji wejścia/wyjścia, w tym operacji Reset(plik). Kontrola ta polega na sygnalizacji występującego błędu i przerwaniu działania programu (tak jest standardowo). Zaś przy dyrektywie {$I-} błędy wejścia/wyjścia nie powodują przerwania działania programu, ale po wystąpieniu błędu te operacje są ignorowane aż do wywołania standardowej funkcji lOResult, która kasuje stan błędu. Jeśli w trakcie wykonywania programu nie wystąpił błąd operacji wejścia/wyjścia, to IOResult=0, a w przeciwnym razie IOResult<>0.
Poniżej opisaliśmy moduł uWeWy, który można stosować w programach, korzystających z osobnych plików z danymi i wynikami. Uwzględniliśmy w nim również obsługę błędów wejścia/wyjścia.
unit uWeWy; {plik uWeWy}
interface
procedurę otworzWe(var we:Text);
procedurę otworzWy(var wy:Text);
implementation
var nazwa:string; {nazwa pliku}
OK:boolean;
{Sprawdzanie poprawności wykonania operacji wejścia i wyjścia w jeżyku Pascal.}
procedure otworzWe(var we:Text);
begin
repeat
Write('Podaj nazwę pliku z danymi (puste-koniec): `);
Readln(nazwa); if nazwa='' then Halt;
Assign(we,nazwa);
{Dyraktywa kompilatora {$1+} lub {$1-} jest dyrektywą lokalną, tzn. można ją wstawić w dowolne miejsce}
{$1-} Reset(we); {$1+} OK:=IOResult<>0;
if OK then Writeln('Nie można odnaleźć pliku.');
until not OK
end; {otwórzWe}
procedure otworzWy(var wy:Text);
begin
Write('Podaj nazwę pliku z wynikami: ');
Readln(nazwa); Assign(wy,nazwa); Rewrite(wy)
end; {otwórzWy}
end.