Programowanie w języku Pascal

Pliki w Turbo Pascalu

Aby poprawnie operować funkcjami zapisu i odczytu plików należy zapoznać się z ich typami w Pascalu:
 
- binarne typowane, zwane także rekordowymi,
- binarne beztypowe
- tekstowe

Uwaga:
Liczba w nawiasie kwadratowym [1] oznacza numer akapitu przykładowego programu.


Kod ASCII:

Jest to sposób kodowania informacji, który każdej cyfrze od 0-255 (zakres jednego bajtu) przyporządkowuje określony znak. Liczbą od 32 do 126 przyporządkowane są znaki alfabetu arabskiego (duże i małe litery, cyfry, znaki interpunkcyjne). Liczby 0-31 i 127 są to znaki sterujące np. znak początku nowej linii [ENTER], początek transmisji, koniec strony itp. Powstał na przełomie lat 50 i 60 w celu ustandaryzowania sposobu wymiany i zapisu danych pomiędzy urządzeniami typu: drukarka, faks, monitory itp.


1. Pliki binarne:

Reprezentacja poszczególnych bajtów pliku jest zgodna z ich reprezentacją w pamięci komputera. Program (lub procedura) zapisujący i odczytujący musi być świadomy organizacji danych w pliku (wielkości rekordu, typu danych w rekordzie itp.)

a.) Pliki typowane:
Plik tego typu jest złożeniem rekordów o tej samej długości, odczytywanych w identyczny sposób. Wielkość pliku musi być wielokrotnością rekordu.
Przykładowo:
Powiedzmy, że plik ma 100 bajtów. Zakładając, że rekord składa się z 10 bajtów, plik musi zawierać 100/10, czyli 10 rekordów. Pierwsze sześć bajtów każdego rekordu odczytujemy jako zmienną łańcuchową, następne dwa jako zmienną typu WORD, bajt dziewiąty i dziesiąty jako zmienną BYTE. Rekord jest najmniejszym elementem pliku, chociaż można ominąć tą niedogodność deklarując przykładowo pierwsze szesc bajtów powyższego rekordu jako zmienne char. Za najmniejszy element uważamy taki, który można zapisać lub odczytać standardową procedurą.

b.) Pliki beztypowe:
Dane zapisywane są w sposób ciągły, nie ma rozróżnienia na rekordy (dokładniej może składać się z rekordów, ale każdy z nich może mieć inną wielkość). Programista może odwołać się do dowolnego bajtu pliku. Są to najczęściej występujące i najbardziej uniwersalne typy plików. W sposób beztypowy zapisywane są pliki programów, graficzne, muzyczne itp. Ponieważ ich obsługa jest trochę bardziej skomplikowana i raczej żadko będziemy z nich korzystać ich opis zostanie pominięty.

2. Pliki tekstowe:

Każdy bajt interpretowany jest jako znak w kodzie ASCII. Najmniejszym elementem jaki można odczytać jest linia, czyli ciąg znaków ograniczony znakiem końca linii. Zawartość takich plików można odczytać w bezpośredni sposób w edytorze tekstów.


Programowi jest zasadniczo obojętne, jakiego typu plik otwiera. Spróbuj otworzyć plik np. *.exe, *.bmp, *.gif w edytorze tekstów (wyświetla zawartość pliku beztypowego jako tekstowego). Program robi to bez większego oporu, tyle tylko, że wyświetlana zawartość jest bez sensu.
Możesz także otworzyć dowolny plik w programie książka telefoniczna.

Plik który został zapisany jako tekstowy, można poprawnie odczytać deklarując go np. jako file of char



Kursor plikowy – z aktualnym położeniem kursora plikowego związana jest bieżąca operacja zapisu lub odczytu. Może leżeć tylko w pozycji będącej wielokrotnością najmniejszego elementu.


Operacja plikowa w Pascalu przebiega w czterech etapach:

1.) przypisanie zmiennej plikowej do określonego pliku

Dokonuje tego procedurą :assign zdefiniowana: assign (var f ;string), gdzie „f” to nazwa zmiennej, która będzie identyfikować w całym programie plik o nazwie określonej przez string. Procedura assign jest wspólna dla plików binarnych i tekstowych. [17]. Deklaracja zmiennej plikowej f ma następującą postać:

Var
F : File;                                         // dla pliku beztypowego
FF : File of Tksiazkatel;        // dla pliku typowanego
FF : Text;                                        // dla pliku tekstowego


2.) otwarcie lub utworzenie i otwarcie pliku określanego przez zmienną.

Otwarcie (i zamknięcie) pliku wynika z metod obsługi plików przez system operacyjny
z którego funkcji korzysta Pascal.

Dla plików typowanych, nietypowanych i tekstowych używamy tych samych procedur: reset (zmienna_plikowa) i rewrite (zmienna_plikowa).

Pierwsza z nich otwiera istniejący plik. Jeżeli plik jest już otwarty procedura reset go zamyka a następnie otwiera. Dla plików typowanych i nietypowanych reset [19]otwiera plik zarówno do odczytu jak i zapisu, pliki tekstowe są otwierane tylko do odczytu. W przypadku niepowodzenia [20] (najczęściej nieistniejący plik skojarzony ze zmienną) status operacji IO przyjmuje wartość inną od 0.

Procedura rewrite [20] tworzy plik a następnie go otwiera. Jeżeli plik o danej nazwie już istniał, zostanie on skasowany a w jego miejsce zostanie utworzony nowy plik. Jest logiczne,
że w typ przypadku plik może zostać otwarty tylko do zapisu.

Procedurą otwierającą dla plików tekstowych: jest append. Otwiera ona plik a następnie ustawia kursor plikowy na końcu pliku. Append umożliwia dopisywanie do pliku.

3.) zapis i odczyt z pliku

Do odczytywania elementów plików typowanych służy funkcja read [94]. W przypadku pliku typowanego procedura read odczytuje pobiera porcje danych której wielkość zdeterminowana jest deklaracją typu pliku.
Przykład:
Jeżeli plik zadeklarowany jest jako file of integer, read pobiera dwa bajty danych. Po wykonaniu procedury read wartość kursor plikowy ustawia się na początku nowego elementu.
W przypadku plików tekstowych działanie funkcji read jest następujące: odczytywane są wszystkie znaki do pierwszego napotkanego znaku końca linii (lub końca pliku). Po wykonaniu pierwszej instrukcji read wszystkie następne Read będą widzieć znak końca linii
i będą zwracały pusty łańcuch. Pokazuje to program poniżej:

program read;
uses crt;
 
var
plik : text;
nazwapliku,bufor : string;
 
begin
clrscr;
readln(nazwapliku);
{$I-}
assign (plik,nazwapliku);
reset (plik);
 
while not eof(plik) do
begin
 read (plik, bufor);
 writeln(bufor);
  repeat until keypressed
 end;
close(plik);
end.


Specjalnie na potrzeby plików tekstowych stworzona została procedura readln.
Działanie procedury readln przebiega następująco: wykonanie procedury read, ustawienie kursora plikowego na początku nowej linii.
Procedura readln jest używana tylko i wyłącznie podczas odczytu plików tekstowych.


Procedury zapisu to: write i writeln. Ich działanie jest zbliżone do procedur odczytu. Skrótowo:

Write zapisuje rekord i ustawia kursor zaraz za zapisanym danymi.

W przypadku pliku tekstowego zapisuje daną lub kilka danych ( np. write(plik,dana1,dana)) w sposób ciągły, bez wstawiania znaku końca linii.
Przykładowy program:

uses crt;
 var
  F: Text;
  Ch,cc,c: string;
  i :integer;
 
 begin
  clrscr;
  readln(ch);
  assign(f,ch);
  rewrite(F);
  readln(c);
  readln(ch);
  for I:=1 to 5 do
  write(F, C,ch,i);
  close(f);
end.



Uwaga:
Nie ma znaczenia jakiego typu jest dana, w pliku zostanie zapisana jej wartość tzn. i:=1
(i typu integer) zostanie zapisane jako kod ASCII który odpowiada znakowi „1”. Możesz się o tym przekonać uruchamiając powyższy program.

Writeln zapisuje łańcuch znaków na jego końcu, wstawia znak końca linii, ustawia kursor zaraz po nim.


Co zrobić, jeżeli chcemy odczytać lub zapisać dane w miejscu innym niż koniec pliku?

W plikach tekstowych nie ma możliwości dopisania danych np. w trzeciej linijce od końca. Nie jest to dziwne, ponieważ każda linijka ma nieokreśloną długość. Najlepiej wyjaśnić to na przykładzie. Niech spacja oznacza znak końca linii, tak więc:

[Litwo ojczyzno moja] //plik, którego 2 linijke zamienimy na ‘Adam Mickiewicz’
[Litwo AdamMickiewicz moja] //to powinien być spodziewany wynik
[Litwo AdamMickiew] // Rzeczywisty wygląd pliku

Na szczęście o poprawność (sensowność) tego typu operacji dba za nas kompilator, więc powyższa operacja nie miałaby szans powodzenia. Sens ma jedynie zapis na końcu pliku.

Aby odczytać n-tą linijkę tekstu trzeba odczytać wszystkie poprzednie linijki, lub posłużyć się funkcją seekeoln. Przykład:
For I=0 to 5 do seekeoln;
Readln (plik, zmienna);

Inaczej ma się sprawa z plikami typowanymi. Ilość danych odczytywanych lub zapisywanych ma zawsze stałą wielkość, więc nie powinno być z tym problemu. Służy do tego funkcja procedura seek(plik,N) [24]. Ustawia ona pozycje kursora plikowego na pozycji N-tego rekordu. Funkcja filesize zwraca liczbe rekordów w pliku.

4.) zamknięcie pliku

Operacja, o której nie można zapomnieć. Jej niewykonanie może spowodować utratę danych, co jest związane buforowaniem zapisu.

Dokonuje się tego procedurą Close (zmienna_plikowa);


Obsługa błędów:

Standardowo wystąpienie błędu wejścia-wyjścia (zła nazwa pliku, źle określony dysk, zabezpieczenie przed zapisem itp.) prowadzi najczęściej do wyświetlania komunikatu i zakończeniu pracy programu. Nie jest to zbyt fortunny sposób. Aby przejąć kontrole nad obsługą błędów we-wy należy użyć przełącznika {$I-} przed możliwym miejscem wystąpienia błędu. Przełącznik ten blokuje generowanie kodu sprawdzającego rezultat wykonania operacji we-wy. Rezultat wykonania ostatniej operacji odczytujemy funkcją IOResult. Jeżeli zwróci ona wartość równą 0 oznacza to, że wykonanie operacji we-wy przebiegło pomyślnie. Każdej innej wartości przyporządkowany jest określony typ błędu. Przykładowy algorytm obsługi zaczyna się od akapitu [18].


program Ksiazka_tel;
 
uses crt;
 
type
  TKsiazkatel = record
  Imie, Nazwisko : string[10];
  NumerTel : string[15];
end;
 
var
  Ksiazkatel : Tksiazkatel;
  plik : file of Tksiazkatel;
  NP : string;
  cozrobic : string;
  label aaa;
 
procedure main ;forward;
 
function otworzplik (var nazwapliku : string) : byte;
 
begin
assign (plik,nazwapliku);
{$I-}
reset (plik);        
if ioresult<>0 then rewrite(plik);
if ioresult=0 then otworzplik:=0 else
otworzplik:=1;
{$I+}
seek(plik,filesize(plik));
end;
 
procedure zapisdopliku;
begin
write (plik,ksiazkatel);
end;
 
procedure koniec ; forward;
 
procedure pobierzdane;
begin
write ('Imie       ');Readln(ksiazkatel.imie);
if ksiazkatel.imie=' ' then koniec;
write ('Nazwisko   ');readln(ksiazkatel.nazwisko);
write ('Numer tel. ');Readln(ksiazkatel.numertel);
writeln;
zapisdopliku;
pobierzdane;
end;
 
procedure koniec;
begin
close(Plik);
main;
end;
 
procedure zapis;
begin
clrscr;
writeln ('Nazwa pliku powinna zawiera sciezke, 
               jesli jej nie      podasz');
writeln ('plik zostanie utworzony w katalogu bierzacym.');
writeln ('Obowiazuje skladnia 8+3. Spacja w polu imie');
writeln ('spowoduje wyjscie do menu glownego.');
Writeln ('Jezeli plik juz istniej, dane zostana do niego
               dopisane');
writeln ;
write ('Podaj nazwe pliku:  ');
readln (NP);
if otworzplik (NP)=1 then
begin
writeln('nieznany blad');
end else
begin
writeln;
writeln ('Wpisz dane');
pobierzdane;
end;
end;
 
procedure odczyt;
var
Plik : file of  TKsiazkatel;
nazwapliku : string;
label aaa;
begin
clrscr;
aaa:
writeln ('Nazwa pliku powinna zawierac scieľke,
                je˜li jej nie podasz');
writeln ('plik zostanie otwarty w katalogu bierzacym.');
writeln ('Obowiazuje skladnia 8+3. ');
writeln;
write ('? ');
readln(nazwapliku);
writeln;
{$I-}
        assign (plik,nazwapliku);
reset (plik);
if ioresult<>0 then
begin
writeln ('zla nazwa pliku');
writeln;
goto aaa;
end else
begin
while not Eof(Plik)  do
begin
read (plik,ksiazkatel);
writeln  
              (ksiazkatel.imie,' ',ksiazkatel.nazwisko,' ',ksiazkatel.numertel);
end;
close (Plik);
writeln;
writeln ('nacisniecie jakiegokolwiek klawisza');
writeln ('powrot do menu glownego');
repeat until keypressed;
main;
end;
end;
 
procedure main;
label aaa;
begin
cozrobic:='';
clrscr;
writeln;
writeln ('Ksiazka Telefoniczna',#10,#13,'made by JoeBar');
writeln ;
writeln ('Jaka operacje chcesz wykonac?');
gotoxy (1,19);
writeln ('Napisany i skompilowany 07.03.2003');
gotoxy (3,7);
write ('1-zapis');
aaa:
gotoxy (3,10);
write ('2-odczyt');
gotoxy (3,13);
writeln ('3-koniec');
writeln;
write ('? ');
readln (cozrobic);
if cozrobic ='3' then halt;
if cozrobic ='1' then zapis;
if cozrobic ='2' then odczyt else goto aaa;
end;
 
begin
main;
end.


Sprawdźmy działanie programu &#8222;książka telefoniczna&#8221;.
Uruchom go, wybierz opcje zapisu. Następnie wpisz :

&#8222;Jan&#8221;, &#8222;Kowalski&#8221;, &#8221;111&#8221;, &#8221;Anna&#8221;, &#8221;Kowalska&#8221;, &#8221;222&#8221;.

Otwórz np. w Notepadzie plik w którym zostały zapisane dane. Otrzymaliśmy:

[ Jan....... Kowalski.. 111............ Anna...... Kowalska.. 222............].  

Pole imie powinno składać się z 11 znaków (zaczynamy liczyć od zera, zerowy znak zawiera długość łańcucha), nazwisko i numer 16 znaków. Jak się okazuje tak jest w rzeczywistości.

Powyższe procedury są poprawne także dla Delphi, jednak w tym środowisku są to funkcje przestarzałe. W Delphi zostały stworzone nowe procedury korzystające z funkcji windows.

3 komentarze

Żabie Udka 2009-06-07 19:04

<quote>typ beztypowy?</quote>
Coś mi się wydaje że takiego coś nie istnieje. Jedynie istnieje typ amorficzny w tym języku

Valarius 2006-01-01 16:25

Mnie ucza, zeby nigdy nie stosowac goto :)

fatalbomb 2005-12-31 18:09

Ale my nie używamy liter arabskich, tylko łacińskich. Arabskie są cyfry.