Kurs Pascal - VI. Rekordy i operacje na plikach

D.F.

Były Moderator
Dołączył
Listopad 4, 2009
Posty
493
I. Wstęp

W poprzedniej części poznałeś tablice. Szósta część kursu została poświęcona na omówienie kolejnego typu danych jakim jest rekord. W przeciwieństwie do tablic, w rekordach można przechowywać elementy różnych typów (napis, liczbę itp.). Poszczególny element rekordu nazywany jest polem. Poznasz również jak wykonywać różne operacje na plikach.


II. Rekordy

Rekordy możemy zadeklarować jako typ oraz jako zmienną. Deklarując rekord jako typ, możemy później tworzyć wiele zmiennych tego typu. Zaraz sam zobaczysz.

Deklaracja rekordu jako typu wygląda następująco:

Kod:
 type
   TRekord = record
     Napis : String;
     Liczba : Integer;
   end;

Po zadeklarowaniu własnego typu rekordowego możemy stworzyć zmienną tego typu (można też tworzyć np. tablicę rekordów):

Kod:
 var
   R : TRekord; //zmienna R typu TRekord

Wtedy do poszczególnych pól tego rekordu (zmiennej) odwołujemy się następująco:

Kod:
 R.Napis := 'jakiś tekst';
 R.Liczba := 12345;

Rekordy możemy również deklarować bezpośrednio w sekcji ze zmiennymi w ten sposób:

Kod:
 var
   R : record
     Napis : String;
     Napis2 : String;
     Liczba : Integer;
   end;

Poniżej przedstawiam prosty program pytający użytkownika o podanie kilku informacji o sobie, zapisujący je do poszczególnych pól rekordu, a następnie wyświetlający te dane:

Kod:
 program Rekordy1;

 uses CRT;

 //sekcja z deklaracjami typów
 type
   //deklaracja typu rekordowego TUser
   TUser = record
     //poszczególne pola rekordu
     NameSurname : String;
     BirthYear : Integer;
     Interestings : String;
   end;

 var
   U : TUser; //zmienna U, typu TUser
 begin
   ClrScr();

   //zapytaj użytkownika o imię i nazwisko,
   //a następnie przypisz je do pola NameSurname
   WriteLn('Imię i nazwisko:');
   ReadLn(U.NameSurname);

   //zapytaj o rok urodzenia i przypisz do pola BirthYear
   WriteLn('Rok urodzenia:');
   ReadLn(U.BirthYear);

   //zapytaj o zainteresowania i przypisz do pola Interestings
   WriteLn('Zainteresowania:');
   ReadLn(U.Interestings);

   WriteLn();

   //wyświetl wprowadzone wcześniej dane
   WriteLn('Wprowadziłeś następujące dane:');
   WriteLn('Imię i nazwisko: ', U.NameSurname);
   WriteLn('Rok urodzenia: ',U.BirthYear);
   WriteLn('Zainteresowania: ',U.Interestings);

   ReadLn();
 end.


III. Pliki

Biblioteka języka Pascal zawiera gotowe procedury i funkcje do wykonywania różnych operacji na plikach. Przedstawię teraz najważniejsze z nich. Pod każdą procedurą i funkcją podane jest jak wygląda jej deklaracja w module. Daje nam to informacje na temat przyjmowanych parametrów przez procedurę oraz przyjmowanych argumentów i zwracanych wartości przez funkcję.



Append - Otwiera istniejący plik i ustawia wskaźnik na końcu pliku w celu dopisania danych do pliku. Tylko pliki tekstowe.

Składnia:

Kod:
procedure Append(
   var t: Text
 );



Assign - Kojarzy nazwę pliku ze zmienną plikową (nie otwiera pliku). Wszystkie rodzaje plików.

Składnia:

Kod:
procedure Assign(
   var f: file;
   const Name: String
 );

 procedure Assign(
   var f: file;
   p: PChar
 );

 procedure Assign(
   var f: file;
   c: Char
 );

 procedure Assign(
   var f: TypedFile;
   const Name: String
 );

 procedure Assign(
   var f: TypedFile;
   p: PChar
 );

 procedure Assign(
   var f: TypedFile;
   c: Char
 );

 procedure Assign(
   var t: Text;
   const s: String
 );

 procedure Assign(
   var t: Text;
   p: PChar
 );

 procedure Assign(
   var t: Text;
   c: Char
 );



Close - Czyści wewnętrzny bufor pliku i zamyka plik. Aby otworzyć zamknięty plik, nie trzeba wywoływać Assign od nowa, wystarczy użyć Reset lub Rewrite.

Składnia:

Kod:
procedure Close(
   var f: file
 );

 procedure Close(
   var t: Text
 );



EOF - funkcja zwraca wartość Prawda (True), jeżeli wskaźnik ustawiony jest na końcu aktualnie otwartego pliku lub plik jest pusty.

Składnia:
Kod:
function EOF(
   var f: file;
 ):Boolean; function EOF(
   var t: Text;
 ):Boolean;
 function EOF: Boolean;



Flush - Zapisuje dane z bufora pliku na dysk.

Składnia:

Kod:
 procedure Flush(
   var t: Text
 );



Reset - Otwiera plik do odczytu. Plik może być dowolnego typu (tekstowy, typu określonego lub nieokreślonego). Jeżeli jest to plik tekstowy zostaje otwarty tylko do odczytu, w przeciwnym wypadku jest otwierany zależnie od wartości zmiennej globalnej FileMode (wartość 2 to odczyt-zapis).

Składnia:

Kod:
 procedure Reset(
   var f: file;
   l: LongInt
 );

 procedure Reset(
   var f: file
 );

 procedure Reset(
   var f: TypedFile
 );

 procedure Reset(
   var t: Text
 );



Rewrite - Otwiera plik do zapisu. Usuwa jego poprzednią zawartość. Plik może być dowolnego typu. Jeżeli typ pliku jest określony lub nieokreślony, zostaje on otwarty do odczytu i zapisu.

Składnia:

Kod:
 procedure Rewrite(
   var f: file;
   l: LongInt
 );

 procedure Rewrite(
   var f: file
 );

 procedure Rewrite(
   var f: TypedFile
 );

 procedure Rewrite(
   var t: Text
 );



Seek - ustawia aktualną pozycję wskaźnika w pliku. Pierwszy parametr to zmienna plikowa, drugi to nowa pozycja wskaźnika.

Składnia:
Kod:
procedure Seek(
 var f: file;
 Pos: Int64
);



BlockWrite - zapisuje dane do pliku nieokreślonego typu. Pierwszy parametr to zmienna plikowa, drugi to bufor z danymi, trzeci to ilość zapisywanych danych.

Składnia:
Kod:
procedure BlockWrite(
 var f: file;
 const Buf;
 Count: Int64;
 var Result: Int64
);

 procedure BlockWrite(
 var f: file;
 const Buf;
 Count: LongInt;
 var Result: LongInt
);

 procedure BlockWrite(
 var f: file;
 const Buf;
 Count: Cardinal;
 var Result: Cardinal
);

 procedure BlockWrite(
 var f: file;
 const Buf;
 Count: Word;
 var Result: Word
);

 procedure BlockWrite(
 var f: file;
 const Buf;
 Count: Word;
 var Result: Integer
);

 procedure BlockWrite(
 var f: file;
 const Buf;
 Count: LongInt
);



BlockRead - odczytuje dane z pliku nieokreślonego typu do pamięci. Pierwszy parametr to zmienna plikowa, drugi to bufor na dane, trzeci to ilość odczytywanych danych.

Składnia:
Kod:
procedure BlockRead(
 var f: file;
 var Buf;
 count: Int64;
 var Result: Int64
);

 procedure BlockRead(
 var f: file;
 var Buf;
 count: LongInt;
 var Result: LongInt
);

 procedure BlockRead(
 var f: file;
 var Buf;
 count: Cardinal;
 var Result: Cardinal
);

 procedure BlockRead(
 var f: file;
 var Buf;
 count: Word;
 var Result: Word
);

 procedure BlockRead(
 var f: file;
 var Buf;
 count: Word;
 var Result: Integer
);

 procedure BlockRead(
 var f: file;
 var Buf;
 count: Int64
);


Pliki tekstowe

Ten rodzaj plików przechowuje tekst podzielony na linijki. Obsługa tego rodzaju plików jest najłatwiejsza.

Poszczególne czynności jakie wykonamy:
- Skojarzenie nazwy pliku ze zmienną plikową procedurą Assign
- Otwarcie pliku do odczytu/zapisu procedurą Reset, Append lub Rewrite
- Zapis lub odczyt danych z pliku/do pliku (procedury: Write, WriteLn, Read, ReadLn...)
- Zamknięcie pliku procedurą Close

Oto przykładowy program dopisujący dane do pliku:

Kod:
 program PlikiTekstowe1;

 uses CRT;

 var
   F : TextFile;
 begin
   WriteLn('Program zapisujący dane do pliku.');

   Assign(F, 'D:\plik.txt');
   Rewrite(F);
   WriteLn(F,'pierwsza linijka');
   WriteLn(F,'druga linijka');
   WriteLn(F,'trzecia linijka');
   Close(F);

   ReadLn();
 end.

Najpierw wyświetlany jest tekst na konsoli. Następnie nazwa pliku jest kojarzona ze zmienną plikową F. Dalej następuje otwarcie pliku do zapisu z wyczyszczeniem jego poprzedniej zawartości (procedura Rewrite). W kolejnych trzech linijkach procedurą WriteLn z podaną zmienną plikową jako pierwszy parametr dopisujemy poszczególne linijki do pliku. Na końcu zamykamy plik procedurą CloseFile.

Natomiast poniższy kod odczytuje całą zawartość pliku i wyświetla ją w oknie konsoli:

Kod:
 program PlikiTekstowe2;

 uses CRT;

 var
   F : TextFile;
   S : String;
 begin
   WriteLn('Program odczytujący dane z pliku.');

   Assign(F, 'D:\plik.txt');
   Reset(F);
   while not EOF(F) do
   begin
     ReadLn(F,S);
     WriteLn(S);
   end;
   Close(F);

   ReadLn();
 end.

W powyższym warta omówienia jest pętla while wyświetlająca tekst. Pętla za każdym swoim wykonaniem wywołuje funkcję EOF, która zwraca wartość Prawda (True), jeżeli jest już koniec pliku. My negując instrukcją logiczną not zwracaną wartość sprawiamy, że pętla wykonuje się dopóki wskaźnik pliku nie został ustawiony na koniec. Wewnątrz pętli procedura ReadLn odczytuje linijkę z pliku zapisując ją do zmiennej S i przesuwa wskaźnik pliku linijkę niżej, dalej procedura WriteLn wyświetla ostatnio odczytaną linijkę.


Pliki określonego typu

Operowanie na tych plikach jest nieco trudniejsze. Tak jak plik tekstowy był podzielony na linię, plik tego typu jest podzielony na części określonego przez nas rozmiaru.

Dla przykładu, deklarując plik w ten sposób:

Kod:
var
   F : file of Byte;

Określamy plik zawierający ciąg bajtów. W powyższej deklaracji zamiast typu Byte, możemy użyć dowolnego typu danych, wtedy plik będzie zawierał ciąg danych tego typu.

Na zwrócenie uwagi zasługuję plik zawierający rekordy. Pozwala nam to w dość łatwy sposób stworzyć prostą bazę danych.

Zobacz, więc na kod tego programu do przechowywania numerów telefonów:

Kod:
 program PlikiOkreslonegoTypu1;

 uses CRT;

 type
   TKlient = record
     ImieNazwisko : String[50];
     NumerTel : LongInt;
   end;

 var
   F : file of TKlient;
   Klient : TKlient;
   C : Char;
 begin
   ClrScr();

   //skojarz plik i otwórz do odczytu i zapisu
   Assign(F, 'D:\plik.db');
   FileMode := 2;
   Reset(F);

   //wyświetl rekordy z pliku
   Seek(F, 0);
   while not EOF(F) do
   begin
     Read(F,Klient);
     WriteLn(Klient.ImieNazwisko,' tel. ',Klient.NumerTel);
   end;

   WriteLn();
   WriteLn('Czy chcesz dodać nowy wpis? t/n');
   ReadLn(C);
   if (C = 't') or (C = 'T') then
   begin
     WriteLn('Imię i nazwisko:');
     ReadLn(Klient.ImieNazwisko);
     WriteLn('Numer telefonu:');
     ReadLn(Klient.NumerTel);

     //dopisz nowy rekord do pliku
     Seek(F, FileSize(F));
     Write(F, Klient);
   end;

   Close(F);
 end.

Najpierw deklarujemy typ TKlient, który jest rekordem składającym się z dwóch pól - jedno będzie zawierało imię i nazwisko osoby, drugie numer telefonu. Niżej w sekcji ze zmiennymi widzimy zmienną plikową typu TKlient (plik zawierający rekordy TKlient), zwykłą zmienną typu TKlient (do zapisania wprowadzonych danych, które następnie zapiszemy do pliku) oraz zmienną C typu Char (znak) do przechowania znaku wprowadzonego przez użytkownika (odpowiedzi t lub n na pytanie zadane użytkownikowi).
Przejdźmy do kodu głównego. Czyścimy ekran konsoli. Kojarzymy zmienną plikową z nazwą pliku (procedura Assign). Ustawiamy typ dostępu do pliku (FileMode := 2, czyli odczyt-zapis) i otwieramy plik procedurą Reset. Dalej procedurą Seek ustawiamy wskaźnik na początku pliku (podajemy zero jako pozycję) i w pętli while odczytujemy rekordy z pliku. Po wyświetleniu rekordów z pliku, użytkownik pytany jest czy chce dodać nowy rekord. Sprawdzana jest wartość zmiennej C, jeżeli użytkownik wpisał literę t lub T, to prosimy o podanie imienia i nazwiska, które zapisujemy w polu ImieNazwisko rekordu Klient, a podany numer telefonu zapisujemy w polu NumerTel, również rekordu TKlient. Następnie ustawiamy wskaźnik na końcu pliku (procedura Seek), ponieważ chcemy dopisać do niego nowy rekord. Następnie procedurą Write dopisujemy rekord Klient do zmiennej plikowej F. Na końcu procedura Close czyści bufor wewnętrzny zmiennej plikowej, zapisuje dane na dysk (gdybyśmy pominęli tą procedurę, dane mogłyby nie zostać zapisane) i zamyka plik.


Pliki nieokreślonego typu

Pliki nieokreślonego typu zawierają różne dane. Nie są podzielone na linijki, tak jak pliki tekstowe, ani na takie same części jak pliki określonego typu. Pliki nieokreślonego typu to np. plik z obrazem, plik wykonywalny, plik dźwiękowy, czy plik z danymi jakiegoś programu. Do odczytu danych z tego typu plików służy procedura BlockRead, natomiast do zapisu BlockWrite.

Na początek, zapiszmy do pliku różnego rodzaju dane, które następnie odczytamy.

Kod:
 program PlikiNieokreslonegoTypu1;

 uses CRT;

 var
   F : File;
   S : String[32];
   I : Integer;
   R : record
         A : array[0..5] of Char;
         S1, S2 : String[24];
       end;
 begin
   S := 'przykladowytekst';
   I := 12345;

   R.A[0] := 'A';
   R.A[1] := 'B';
   R.A[2] := 'C';
   R.A[3] := 'D';
   R.A[4] := 'E';
   R.A[5] := 'F';

   R.S1 := 'tekst z rekordu';
   R.S2 := 'drugi tekst z rekordu';

   Assign(F, 'D:\plik.dat');
   Rewrite(F, 1);

   BlockWrite(F,S,32);
   BlockWrite(F,I,2);
   BlockWrite(F,R,54);

   Close(F);

   ReadLn();
 end.

W powyższym programie widzimy zadeklarowaną zmienną plikową nieokreślonego typu. Mamy zadeklarowane różne dane, które będą zapisane do pliku: zmienną zawierającą napis (String), zmienną typu Integer oraz rekord zawierający trzy pola, gdzie pierwsze to sześcioelementowa tablica (od 0 do 5) znaków, a drugie i trzecie pole to zmienne typu String. Na początku w kodzie programu widzimy przypisanie wartości zmiennym. Dalej jest skojarzenie pliku ze zmienną plikową (Assign), otwarcie pliku do zapisu (Rewrite występuje tutaj z dodatkowym parametrem określającym o ile bajtów będzie przemieszczać się przy pojedynczym przesunięciu wskaźnik pliku). Następnie widzimy trzy procedury BlockWrite, które zapisują do pliku F zmienne: S, I oraz R. Jako trzeci parametr w procedurze BlockWrite podajemy ile bajtów ma zostać zapisane do pliku.
Spróbuj skompilować ten program i wykonać, a następnie obejrzyj budowę utworzonego przez ten program pliku w edytorze heksadecymalnym lub nawet notatniku systemowym. Możesz sprawdzić jaki jest rozmiar pliku, powinien on być równy tylu bajtom ile zostało razem podane w wywołaniach procedury BlockWrite.

Teraz spójrz na program odczytujący dane z pliku, które zapisał poprzedni program:

Kod:
 program PlikiNieokreslonegoTypu2;

 uses CRT;

 var
   F : File;
   S : String[32];
   I : Integer;
   R : record
         A : array[0..5] of Char;
         S1, S2 : String[24];
       end;
 begin
   ClrScr();

   Assign(F, 'D:\plik.dat');
   Reset(F, 1);

   BlockRead(F,S,32);
   BlockRead(F,I,2);
   BlockRead(F,R,54);

   WriteLn('S=',S);
   WriteLn('I=',I);
   WriteLn('R.A=',R.A);
   WriteLn('R.S1=',R.S1);
   WriteLn('R.S1=',R.S2);

   Close(F);

   ReadLn();
 end.

Na początku kojarzymy plik ze zmienną plikową, otwieramy procedurą Reset i odczytujemy dane procedurą BlockRead. Dalej następuje wyświetlenie odczytanych danych i zamknięcie pliku. Po wykonaniu tego programu na ekranie powinny się wyświetlić te same dane, co wcześniej zostały zapisane.


IV. Zakończenie

To wszystko w tej części kursu. Poznałeś rekordy i pliki. Kolejna część opisuje tworzenie własnych bibliotek. Gdyby coś było niejasne, napisz w tym temacie.
 
Do góry Bottom