Pliki

ŁF

<font size="-2">

(Tekst w kwadratowych nawiasach pochodzi ode mnie - Łukasz Fronczyk)</span>
</p>

Jesli zazyczysz sobie, aby Twoj program zapisywal cos do pliku (konfiguracje),
lub odczytywal cos z niego to powinienes przede wszystkim okreslic jego typ.
Trzy typy plikow: tekstowe, rekordowe [elementowe] i luzne [amorficzne]
(nazwy sam wymyslilem :-) Pliki tekstowe, jak sama nazwa wskazuje zawieraja
glownie tekst i sa najprostrze do odczytu i zapisu, gdyz uzywa sie do
tego najzwyklejszych readln, read, write i writeln. Pliki rekordowe to
takie, ktore skladaja sie tylko z jednego typu zmiennych (np. byte, lub tez
record of ...) powielonego wiele razy. (np. jeden taki plik moze miec
zapisanych w sobie 400 bajtow, 500 stringow[10], lub 30 record of ... ,
lecz nie moze miec tego wszystkiego na raz.) Taki typ plikow jest idealny
do bazy danych. Ostatni typ (luzny) zawiera w sobie mieszanine roznych
typow. Pliki tego typu to na przyklad BMP, czy CHR [i miliony innych, bo
z takiego typu pliku się najczęściej korzysta].
Aby dobrac sie do jakiegokolwiek pliku nalezy najpierw go przypisac
do jakiejs zmiennej i otworzyc. Po zakonczeniu roboty wypada go takze zamknac.

Pliki tekstowe:

Kazda zmienna odpowiadajaca za plik tekstowy ma typ text:

var f:text;

Aby przypisac plikowi dowolnego typu zmienna nalezy uzyc polecenia assign(zmienna,nazwapliku):

assign(f,'foo.txt');

Od chwili przypisania zmiennej uzywasz tylko jej, a nie nazwy pliku. Teraz,
w zaleznosci od tego, co chcesz robic z plikiem powinienes go otworzyc
odpowiednim poleceniem:

reset(f);       {Jesli chcesz czytac z pliku}
rewrite(f);     {Jesli chcesz zapisac cos do pliku; ta operacja skasuje jego zawartosc!}
append(f);      {Jesli chcesz dopisac cos na koncu pliku}

Jak juz napisalem - do odczytywania pliku tekstowego wystarcza read i readln z jednym
dodatkowym parametrem. Otoz, gdy jako pierwszy parametr read'a podasz zmienna plikowa,
to po wykonaniu procedury w drugim parametrze pojawi sie odczytana wartosc, a kursor
plikowy przesunie sie o tyle bajtow, ile odczytales.

Co to jest kursor plikowy? Kiedy otworzysz plik za pomoca reset to kursor plikowy znajduje
sie na jego poczatku. Zawsze, gdy cos odczytujesz, badz zapisujesz to robisz to w miejscu
rozpoczynajacym sie od kursora plikowego.

Wracajac do tematu: zalozmy, ze chcesz odczytac pierwsze dwie linijki z pliku foo.txt:

var
 f:text;
 s:string;
begin
assign(f,'foo.txt');
reset(F);    {Otwieramy plik do odczytu}
readln(f,s); {Odczytujemy pierwsza linijke}
writeln(s);  {Wyswietlamy ja}
readln(f,s); {Odczytujemy druga linijke}
writeln(s);  {Wyswietlamy ja}
close(f);    {Zamykamy plik}
end.

Tak wlasnie odczytuje sie plik tekstowy linijka po linijce :-)

[W ten sposób można bardzo łatwo wyświetlić zawartość pliku tekstowego, stosując pętlę

while not eof(f) do 
begin
  readln(f,s);
  writeln(s);
end;

]

Oczywiscie nie jest to jedyne zastosowanie. Pliki typu text mozemy odczytywac
tez porcjami danych, np. litera po literze, lub po 10 liter. Uzywamy do tego read(f,x).
Procedura ta nie przezuca kursora plikowego na nastepna linijke:

var
 f:text;
 s:string[10];
 ch:char;
begin
  assign(f,'foo.txt');
  reset(f);
  read(f,s);   {W s znajdziemy 10 pierwszych liter,
             a kursor przeniesie sie na jedenasty znak w pliku.}
  writeln(s);
  read(f,ch);  {Odczytujemy jeden znak}
  writeln(ch);
  read(f,ch);  {I jeszcze jeden :-)}
  writeln(ch); {Przerzucmy teraz kursor na nastepna linijke}
  readln(f);   {I odczytajmy z niej 10 liter}
  read(f,s);
  writeln(s);  {Pora zamknac plik}
  close(f);
end.

Oczywiscie chary i stringi to nie jedyne, co mozna odczytac z pliku. Zalozmy,
ze pierwsza linijka pliku to jakas liczba (tylko i wylacznie liczba!) [jeśli
w pliku będzie coś innego, to efekt będzie taki sam, jak przy wczytywaniu
liczb z klawiatury, kiedy podasz jakąś literę]. Mozemy takze odczytac ja readlnem:

var
  f:text;
  w:word;
begin
  assign(f,'foo.txt');
  reset(f);
  readln(f,w);
  writeln(w);
  close(f);
end.

Mam nadzieje, ze rozumiesz :-)

Pora przejsc do zapisu plikow. Mechanizm jest bardzo podobny, wiec nie bede sie
juz tak rozpisywac. Poprostu zamiast readln podstawiasz writeln, a zamiast read
podstawiasz write, nie zapominajac o odpowiednim otworzeniu plik za pomoca rewrite
(czysci zawartosc pliku), lub append (umieszcza kursor na koncu pliku, nie czyszczac
go). Nie ma mozliwosci otworzenia pliku tekstowego z umieszczeniem kursora na jego
poczatku [czy gdziekolwiek indziej w jego środku].

Pliki rekordowe

Typem pliku rekordowego jest file of xx, gdzie xx jest typem zmiennej, ktorej dane
zawarte sa w pliku i tak mamy np.: pliki file of byte (zawierajace iles-tam bajtow),
file of string[10], zawierajace iles-tam stringow[10], file of record
x,y:integer;c:byte;end;

type Osoba:record
    Nazwisko:string[10];
    telefon:longint;
  end;
var f:file of Osoba;
  o:Osoba;

Przy obsludze plikow rekordowych przydaja sie dwa polecenia - read, oraz write. Readln,
oraz Writeln na nic sie nie przydaja, gdyz w tego typu plikach nie rozrozniamy linijek.
[Plik typu rekordowego NIE MA NIC WSPÓLNEGO Z PLIKIEM TEKSTOWYM poza tym, że i jedno i
drugie jest plikiem; inaczej się je otwiera, inaczej się z nich korzysta, inaczej się
po nich porusza - o czym później]. Na poczatek zapiszmy do pliku dane dwoch osob:

type Osoba:record
    Nazwisko:string[10];
    telefon:longint;
  end;
var f:file of Osoba;
  o:Osoba;
begin
  assign(f,'foo.tel');
  rewrite(f);
  O.Nazwisko:='Kowalski';
  O.Telefon:=5412208;
  write(f,o);
  O.Nazwisko:='Doe';
  O.Telefon:=555321;
  write(f,o);
  close(f);
end.

A teraz je odczytajmy:

type Osoba:record
    Nazwisko:string[10];
    telefon:longint;
  end;
var f:file of Osoba;
  o:Osoba;
begin
  assign(f,'foo.tel');
  reset(f);
  read(f,o);
  writeln(o.Nazwisko+' - ',o.telefon);
  read(f,o); (Nie powinno być read?[MD]) ... powinno , poprawiłem su_seba1
  writeln(o.Nazwisko+' - ',o.telefon);
  close(f);
end.

Gdybys zechcial dopisac jakies nazwisko do listy to nic nie stoi na przeszkodzie,
gdyz mozesz uzyc append! [albo składni reset(f); seek(f,FileSize(f));].

type Osoba:record
    Nazwisko:string[10];
    telefon:longint;
  end;
var f:file of Osoba;
  o:Osoba;
begin
  assign(f,'foo.tel');
  append(f);
  O.Nazwisko:='Kolinko';
  O.Telefon:=070012345;
  write(f,o);
  close(f);
end.

Istnieje jednak dosyc duza roznica miedzy plikami tekstowymi, a rekordowymi.
Otoz w przypadku plikow rekordowych kursor plikowy mozesz przesuwac do przodu
i do tylu w pliku, a nie tylko do przodu. Jak to zrobic? Uzyj polecenia seek(f,x),
gdzie x jest numerem rekordu, na ktorym ustawi sie kursor. Poza tym instrukcja
reset(f) nie otwiera juz pliku tylko do odczytu, ale takze i do zapisu. Dzieki
temu mozemy stworzyc bardzo latwo prosta baze danych:

type Osoba:record
    Nazwisko:string[10];
    telefon:longint;
  end;
var f:file of Osoba;
  o:Osoba;
  x:word;
begin
  assign(f,'foo.tel');
  reset(f);
  writeln('Podaj numer rekordu:');
  readln(x);
  seek(f,x);
  read(f,o);
  writeln(o.Nazwisko+' - ',o.telefon);
  writeln('Podaj nowy numer telefonu:');
  readln(o.telefon);
  seek(f,x);
{musimy wrocic do tego rekordu, bo read
przenioslo nas do nastepnego}
  write(f,o);
  close(f);
end.

Niezle, co? Jak widac najbardziej przydaje sie przy bazach danych, choc nie tylko:-)
{W tej chwili robisz sobie przerwe, wypijasz filizanke kawy, bierzesz gleboki
wdech i kontynuujesz czytanie}

Pliki amorficzne ("luźne")

Czyli takie, ktore nie skladaja sie tylko z okreslonych typow danych, ale maja je
wymieszane. Tutaj juz zaczynaja sie pewne schody. Typem pliku luznego jest file.
Bez zadnych dodatkow. Do jego odczytywania nie wykorzystuje sie juz zadnych read'ow,
a mimo, ze plik otwiera sie tym samym poleceniem, czyli read powinienes podac do
niego dodatkowy parametr - 1. Mozesz dac inna rzecz, ale na poczatek wystarczy ci
najzwyklejsze 1. [Parametr ten oznacza domyślną wielkość rekordu, z którego jest
zbudowany taki plik. Jeśli np.: podasz 2 zamiast 1, to wszystkie skoki (seek),
odczyty/zapisy (BlockRead/BlockWrite), wielkość pliku itp. będą się odbywać o dwa
razy więcej BAJTÓW (nie rekordów); liczbę tą można traktować jako mnożnik, przez który
są wymnażane wszystkie zmienne oznaczające położenie czegoś w pliku, jego rozmiar czy
ilość sdanych do zapisu. Uwaga! Zapominanie o tym parametrze jest notoryczne, a trudne
do wyłapania, bo kompilator nie traktuje składni reset(f) gdzie f : file jako błędnej;
wtedy plik jest resetowany z domyślną wartością 128].

var
  f:file;
begin
  assign(f,'foo.dat');
  reset(f,1);
  {...}
  close(f);
end.

Tym razem procedura read juz Ci nie pomoze. MUSISZ uzyc procedur BlockRead(f,x,siz),
oraz BlockWrite(f,x,siz), gdzie X jest dowolna zmienna, a siz jej wielkoscia w bajtach.

Tak wiec otworzmy plik i zapiszmy do niego jakies smieci..

var
  f:file;
  r:record
    a,b:byte;
    s:string[5];
  end;
begin
  assign(f,'foo.dat');
  rewrite(f,1);
  BlockWrite(f,'ABC',3); 
  BlockWrite(f,999,2); {999=2 bajty}
  r.a:=15;
  r.b:=20;
  s:='ABCDE';
  BlockWrite(f,r,7);
  close(f);
end.

W tej chwili ten plik ma 12 bajtow smieci, ktore za chwile odczytamy :-) Odczytujemy je
tak samo, tylko na odwrot :-)

var
  f:file;
  r:record
    a,b:byte;
    s:string[5];
  end;
  s:string[3];
  w:word;
begin
  assign(f,'foo.dat');
  read(f,1);
  Blockread(f,s,3); 
  writeln(s);
  Blockread(f,w,2);
  writeln(w);
  Blockread(f,r,7);
  writeln(r.a);
  writeln(r.b);
  writeln(r.s);
  close(f);
end.

Mam nadzieje, ze zrozumiales :-). Pora teraz na kilka polecen, ktore Ci sie przydadza:
Filepos - funkcja zwraca pozycje kursora plikowego (bez plików tekstowych);
Eof - przyjmuje wartosc true, gdy kursor plikowy pliku f znajduje sie na jego koncu;
Eol - przyjmuje wartosc true, gry kursor plikowy pliku f znajduje sie na koncu pliku (tylko text);
[FileSize - zwraca rozmiar pliku (bez plików tekstowych);
Erase - usuwa plik;
Rename - zmienia nazwę pliku.

Po bardziej szczegółowe informacje udaj się do helpa Turbo Pascala (Shift+F1)].

Do pocwiczenia:

  • Napisz program, ktory bedzie zliczal poszczegolne litery [albo dużo ambitniej: wyrazy] w pliku
  • Stworz prosta baze danych o twoich najwiekszych wrogach.

<font size="-2">Strona została wykonana przez Tomasza 'Merlina' Kolinko AD 1999 / 2000.
Jest częścią Merlin's programming page. Żadna jej część nie może zostać rozpowszechniana,
ani zmieniana bez zgody autora.</span>

8 komentarzy

a tak w ogóle to type ma składnię ...

type = nazwa itd a nie type:nazwa...

append(f) niestety nie działa na recordach ;/ . Aby coś dopisać trzeba zliczyć ilość zapisanych już recordów i za pomocą procedury seek(zmienna plikowa, int64) ustawić się na ostatnim+1 recordzie i wtedy dopisać.
Możliwe, że TP jeszcze kupował te sztuczki z append(f), ale FPC już tego nie ruszy...

Dobre, ale brakuje mi tu większej ilości przykładów, np. zapisu tablicy rekordów do pliku itp. przydała by się też większa ilość zadanek...

Przeczytałem i musze przyznać bardzo dobre!

no i niecałkiem działa.... jak zrobić aby dopisać coś owszem, ale do listboxa? bo tą metodą nie da rady, kombinowałem już na wiele sposobów oto przykład o co mi chodzi:
listbox1.Items.Add(IntToStr(i+1)); no i tu chcę obok dopisać jeszcze coś z pola np. Edit1.text (ale jest to informacja zmienna jak np. numer telefonu)
kombinowałem coś tak: listbox1.Items.Add(IntToStr(i+1))+ Edit1.text ;/takim sposobem nie da rady bo cała lista będzie z kolejną liczbą, ale o jednym numerze telefonu...... więc raczej nie da rady dopisywać wszystkiego za pomocą "append"

to co umieć trzeba:)

Hmmm... Dobre.