11.2.2003
|
čo sa budeme dnes učiť
- dva typy binárnych súborov:
typové a netypové
- údajový prúd (stream)
Typy súborov
V Delphi môžeme so súbormi pracovať
3 rôznymi spôsobmi:
- starý štýl (z Turbo Pascalu) - sem
patrí aj TextFile
- Windows úroveň práce so súbormi
(najnižšia úroveň) - Delphi ju využívajú
pre svoj súborový typ
- Delphi štýl - trieda TStream = údajový
prúd
Najprv sa zoznámime so starým štýlom,
potom prejdeme na nový štýl - prúdy
Binárne súbory
- postupnosť viet buď rovnakého typu alebo rôznych
typov -- hovoríme o súboroch s pevnou
a premenlivou dĺžkou viet
- jednou operáciou Read alebo Write môžeme
naraz prečítať alebo zapísať viac viet
- zo súboru môžeme súčasne čítať
aj do neho zapisovať, bez toho, aby sme museli niečo
prepínať
- hocikedy môžeme určiť presné miesto
v súbore (pozíciu), odkiaľ budeme čítať,
resp. kam budeme zapisovať (priamy prístup)
- pozícia v súbore sa po
každej operácii čítanie/zápis automaticky
posúva na nasledujúcu vetu (môžeme
so súborom pracovať sekvenčne)
v Pascale sú to dva typy práce s binárnymi
súbormi:
- typové binárne súbory -- už
pri deklarovaní súboru určíme typ
vety a tým aj dĺžku vety -- do súboru
môžeme zapisovať len vety tohto typu a tiež z
neho čítať len do premenných tohto typu
-- pozícia vety v súbore sa vyjadruje
počtom viet, ktoré sú pred danou vetou
(číslovanie od 0 do FileSize-1)
- netypové binárne súbory -- môžeme
do neho zapisovať ľubovoľné údaje (premenné)
a tiež z neho čítať do premenných ľubovoľných
typov -- pri otváraní musíme určiť
veľkosť elementárneho bloku a potom všetky pozície
v súbore a dĺžky budú určované
v týchto blokoch (default je 128)
Typové binárne súbory
type súbor = file of identifikátor_typu; // typ vety
- POZOR - veta nesmie obsahovať žiadne časti s nie
pevnou dĺžkou, napr. dynamické polia, veľké
stringy,
súbory, smerníky a iné dynamické
premenné (môže obsahovať jednoduché
premenné, polia, množiny, záznamy,
krátke reťazce - string[...])
- AssignFile, Reset, Rewrite, CloseFile - rovnaké
ako pre textové súbory
- Read, Write - majú trochu iné pravidlá,
napr. pre
var f:subor;
...
Read(f, premenná_typu_veta_súboru);
Write(f, premenná_typu_veta_súboru);
// pre read aj write môže byť aj viac premenných oddelených čiarkami
príklad:
|
var
f:File of integer;
i,j:integer;
begin
AssignFile(f,'cisla.dat'); Rewrite(f);
for i:=1 to 20 do Write(f,i); // 80 bajtov
// CloseFile(f); - netreba, ak nasleduje Reset
Reset(f); // čítanie od začiatku
for i:=1 to 20 do begin
Read(f,j);
Memo1.Lines.Add(IntToStr(j)); // sekvenčné čítanie
end;
// slušnejšie - po koniec súboru:
Reset(f); // alebo seek(f,0);
while not Eof(f) do begin
Read(f,j);
Memo1.Lines.Add(IntToStr(j));
end;
// nesmie sa použiť readln ani writeln - neexistuje značka konca riadka
// výpis súboru odzadu:
for i:=FileSize(f)-1 downto 0 do begin
Seek(f,i); Read(f,j); // priame čítanie
Memo1.Lines.Add(IntToStr(j));
end;
// pridaj vetu na koniec súboru:
Seek(f,FileSize(f));
// hoci sa ako posledný robil reset, nevadí to a do súboru sa dá aj zapisovať
// - reset a rewrite sa navzájom neblokujú => každý otvorený súbor je
// automaticky aj na čítanie aj na zápis
for i:=21 to 40 do Write(f,i);
// skracovanie súboru
Seek(f,25); Truncate(f); // súbor má teraz vety 0..24
CloseFile(f);
end;
|
procedúra vyhod vyhodí í-tu vetu tak,
že ju nahradí poslednou a poslednú zruší:
|
type
veta = integer;
TSubor = file of veta;
...
procedure vyhod(var f:TSubor; i:integer);
var
v:veta;
begin
Seek(f,FileSize(f)-1); read(f,v); // načítanie poslednej
Seek(f,i); write(f,v); // zápis ako i-tej
Seek(f,FileSize(f)-1); Truncate(f); // vyhodenie poslednej
end;
...
vyhod(f,3); vyhod(f,10); vyhod(f,17); // veta na pozícii 3 obsahuje číslo 4,...
Memo1.Lines.Add('po vyhodení 3,10,17');
Reset(f);
while not Eof(f) do begin
Read(f,j);
Memo1.Lines.Add(IntToStr(j));
end;
|
Takýto príklad bol kedysi na skúške:
V binárnom súbore (file of integer) sú
čísla, ktoré popisujú nakreslenie
binárneho stromu. Súbor obsahuje bloky
= štvorice celých čísel x, y, l, p, ktoré
popisujú vrcholy stromu: x a y sú súradnice
vrcholu, l a p sú čísla blokov (štvoríc)
v súbore, ktoré sú spojené
s daným vrcholom. 0-tý blok je
trojica x, y, v, kde v je číslo toho bloku (štvorice),
v ktorom sa nachádza nasledujúci vrchol
v súbore. Všetky ostatné bloky sú
už štvorice. Ak je nejaké číslo bloku
(štvorice) menšie ako 0, tak sa predpokladá,
že taká štvorica už neexistuje.
časť riešenia:
|
var
f:file of integer;
c:TCanvas;
procedure strom(x0,y0,v:integer);
var
x,y,l,p:integer;
begin
if v<0 then exit;
seek(f,v*4-1);
read(f,x,y,l,p);
c.MoveTo(x0,y0);
c.LineTo(x,y);
strom(x,y,l);
strom(x,y,p);
end;
procedure TForm1.Button1Click(...);
var
x,y,v:integer;
begin
c:=Image1.Canvas;
AssignFile(f,'strom.dat'); Reset(f);
read(f,x,y,v);
strom(x,y,v);
CloseFile(f);
end;
|
pre typové binárne súbory sa
automaticky prepočítava Seek, FilePos, FileSize
podľa dĺžky vety, napr.
- Seek(f,číslo) - nastaví sa v súbore
na bajt číslo*dĺžka_vety
- FileSize(f) = počet_bajtov_súboru
div dĺžka_vety
Netypový binárny súbor
var f:file;
- Pascalu neprezradíme typ vety, teda nerobí
sa žiadna kontrola na typ zapisovanej/čítanej
premennej
- nefunguje read, write - musí sa to robiť inak:
BlockRead(var F: File; var Buf; pocet: integer [;
var vysledok:integer]);
- F - premenná pre netypový súbor
- Buf - nejaká premenná (netypový
parameter, znamená ľubovoľný typ)
- pocet - počet čítaných blokov, typu
integer
- vysledok - skutočne načítaný počet
blokov
- ak sa tento parameter neuvedie a nastane nejaká
chyba, tak program spadne na vstupno-výstupnej
chybe (inak pokračuje a môžeme otestovať, či nastala
chyba)
- BlockWrite(var f: File; var Buf; pocet: integer [;
var vysledok:integer]);
- F - premenná pre netypový súbor
- Buf - nejaká premenná (ľubovoľného
typu)
- pocet - výraz udávajúci počet
zapísaných blokov, typu integer
- vysledok - skutočne zapísaný počet
blokov (rovnako ako pre BlockRead)
- Reset(f,dĺžka_bloku); Rewrite(f,dĺžka_bloku);
- ak sa neuvedie dĺžka_bloku, tak sa predpokladá
128 !!!
- akoby v typových súboroch bolo Reset(f,sizeof(veta))
- Seek, FilePos, FileSize - pracujú v závislosti
na dĺžke bloku
Príklad:
kopírovanie súboru pomocou typového
súboru:
|
var
a,b:file of byte;
x:byte;
begin
AssignFile(a,'a.dat'); Reset(a);
AssignFile(b,'b.dat'); Rewrite(b);
while not Eof(a) do begin
Read(a,x);
Write(b,x)
end;
CloseFile(a);
CloseFile(b);
end;
|
kopírovanie pomocou netypového súboru
- po jednom bajte:
|
var
a,b:file;
x:byte;
begin
AssignFile(a,'a.dat'); Reset(a,1); // 1 bajt = veľkosť bloku
AssignFile(b,'b.dat'); Rewrite(b,1);
while FilePos(a)<FileSize(a) do begin // not Eof(a)
BlockRead(a,x,1);
BlockWrite(b,x,1);
end;
CloseFile(a);
CloseFile(b);
end;
|
kopírovanie pomocou netypového súboru
- použitím pomocnej pamäte:
|
const
dlzka = 32768;
var
a,b:file;
x:array [0..dlzka-1] of byte; // 32 kB buffer = krajšie by bolo pomocou pointra
da,db:integer;
begin
AssignFile(a,'a.dat'); Reset(a,1); // 1 bajt = veľkosť bloku
AssignFile(b,'b.dat'); Rewrite(b,1);
repeat
BlockRead(a,x,dlzka,da);
BlockWrite(b,x,da,db)
until (da=0) or (da<>db); // ak da<>db tak nastala chyba pri zápise
if da>0 then ShowMessage('chyba pri zápise');
CloseFile(a);
CloseFile(b);
end;
|
Vlastná trieda TSubor
vlastná triedu, ktorá "obalí"
netypový súbor:
|
type
TSubor = class
private
f:file;
function getpos:integer;
procedure setpos(p:integer);
function getsize:integer;
procedure setsize(p:integer);
public
constructor Create(m:string; novy:boolean);
destructor Destroy; override;
procedure read(var b; d:integer); virtual;
procedure write(var b; d:integer); virtual;
property position:integer read getpos write setpos;
property size:integer read getsize write setsize;
end;
constructor TSubor.Create(m:string; novy:boolean);
begin
AssignFile(f,m);
if novy then Rewrite(f,1) else Reset(f,1);
end;
destructor TSubor.Destroy;
begin
CloseFile(f);
end;
procedure TSubor.read(var b; d:integer);
begin
BlockRead(f,b,d);
end;
procedure TSubor.write(var b; d:integer);
begin
BlockWrite(f,b,d);
end;
function TSubor.getpos:integer;
begin
Result:=FilePos(f);
end;
procedure TSubor.setpos(p:integer);
begin
Seek(f,p);
end;
function TSubor.getsize:integer;
begin
Result:=FileSize(f);
end;
procedure TSubor.setsize(p:integer);
var
pp:integer;
begin
pp:=FilePos(f);
Seek(f,p); Truncate(f);
if pp>p then pp:=p;
seek(f,pp);
end;
|
a môžeme to použiť:
|
var
f:TSubor;
i:integer;
begin
f:=TSubor.Create('subor.dat',true);
try
for i:=1 to 20 do
f.write(i,sizeof(i));
f.position:=0;
while f.position < f.size do begin // eof
f.read(i,sizeof(i));
Memo1.Lines.Add(inttostr(i));
end;
finally
f.Free; // namiesto CloseFile
end;
end;
|
Dátový prúd - trieda TStream
V Delphi exituje základná trieda Tstream,
ktorá zovšeobecňuje metódy na zapisovanie
a čítanie údajov z nejakého záznamového
média. Táto trieda zakryje implementáciu
fyzického súboru.
TStream je "abstraktná" trieda -
pracujeme buď s jej konkrétnymi podtriedami,
napr. TFileStream, alebo si zrealizujeme vlastnú
podtriedu. TStream uvedené metódy (abstract)
nemá definované len deklarované.
vlastnosti (property):
metódy:
- CopyFrom(Source:TStream; Count:integer):integer;
- Read(var Buffer; Count:integer):integer;
- ReadBuffer(var Buffer; Count:integer); -- chyba,
ak sa nedá
- Seek(Offset:integer; Origin:Word):integer;
- kde Origin == soFromBeginning, soFromCurrent, soFromEnd
- Write(const Buffer; Count:integer):integer;
- WriteBuffer(const Buffer; Count:integer);
dôležité sú odvodené triedy,
napr.
- TFileStream
- constructor Create(const FileName:string; Mode:Word);
- kde Mode:
- fmCreate - vytvorí súbor
- fmOpenRead - len čítanie
- fmOpenWrite - len zápis
- fmOpenReadWrite - aj zápis aj čítanie
- TMemoryStream
- LoadFromFile(const FileName:string);
- LoadFromStream(Stream:TStream);
- SaveToFile(const FileName:string);
- SaveToStream(Stream:TStream);
a posledný príklad môžeme
prepísať pomocou TStreamu takto:
|
var
f:TStream;
i:integer;
begin
f:=TFileStream.Create('subor.dat',fmCreate);
try
for i:=1 to 20 do
f.write(i,sizeof(i));
f.position:=0;
while f.position < f.size do begin
f.read(i,sizeof(i));
Memo1.Lines.Add(inttostr(i));
end;
finally
f.Free;
end;
end;
|
|