Na FreeHostingu Endora běží desítky tisíc webů. Přidejte se ještě dnes!

Vytvořit web zdarma

Na FreeHostingu Endora běží desítky tisíc webů. Přidejte se ještě dnes!

Vytvořit web zdarma

int21h

Prce se soubory v pascalu

Pascal je DOSov programovac jazyk, a proto jsou zkonitosti prce se soubory odvozeny od realizace sprvy soubor v systmu DOS. Tedy, kad soubor meme chpat bu jako textov nebo jako binrn a zle jen na ns, v jakm reimu k nmu budeme pistupovat.
Dal podstatn detail je skutenost, e standardn funkce Turbo pascalu, vetn verze 7.01, neznaj dlouh jmna soubor. Pokud je chceme pouvat, musme si sami napsat rozhran k pslunm slubm DOSu. K dispozici natst je knihovna, kter dan funkce, sice kostrbat, ale implementuje.
Freepascal je novj a jeho standardn funkce LFN znaj. Pokud z jakhokoliv dvodu chceme pouit LFN zakzat, udlme to nastavenm promnn LFNsupport.
LFNsupport:=false;
Implicitn je samozejm true.

Klasick prostedky

Vyjmenoval jsem vechny standardn procedury a funkce jazyka pascal, kter maj co do inn se soubory.
V prvn dce jsou vyjmenovny prostedky, kter lze pout na oba zpsoby prce - na textov i binrn soubory. Vjimkou je funkce Eoln, ji lze pout vlun na textov soubory. Jak se tedy pracuje s textovmi soubory?

Textov soubory

Tento progrmek zkopruje jeden soubor do druhho:
const zdroj = 'text.txt';
      cil   = 'text.new';

var f,g:text;      {chceme-li pracovat se soubory jako textovymi, pouzijeme typ TEXT}
    s:string;
    p:longint;
begin
p:=0;
Assign(f,zdroj);   {prirazeni jmena souboru k promenne - nic jineho nedela}
Assign(g,cil);
Reset(f);          {otevre soubor F}
Rewrite(g);        {zalozi soubor G - jestli uz takovy existuje, bude prepsan}
while not Eof(f) do     {dokud nejsme na konci souboru...}
   begin
   inc(p);
   Readln(f,s);         {z F nacti radku textu}
   Writeln(g,s);        {...a uloz ji do G}
   end;
{hotovo, zpracovali jsme vsechny radky souboru}
Close(f);          {uzavru zdrojovy soubor}
Close(g);          {a cilovy taky}
writeln('Soubor mel ',p,' radek.');
end.
Tento zpsob koprovn m vhodu, e pracujeme po jednotlivch dcch a proto i v TP dokeme zpracovat soubory vt ne 64 KB. Nevhodou je, e typ string udr max. 255 znak. Vyskytne-li se v souboru dka del, jsou vechny znaky za 255. ignorovny. Je nepravdpodobn, e byste se s takovmto textovm souborem setkali, ale stane se vm to, kdy budete tmto zpsobem koprovat jin soubory ne texty (soubory EXE, JPG, atd.)
Vimnte si dku Readln(f,s)
Parametr F zajiuje, e se data natou ze souboru a ne z klvesnice jako obvykle.
Analogicky funguje parametr G u pkazu Writeln(g,s)
Soubory, kter otvrme jako textov, jsou obvykle prost text - jen vjimen maj njakou vnitn strukturu. Raritn se to ale pihodit me:
Pedpokldejme , e mte takovto soubor SACHISTI.TXT:
ELO vek jmno
-----------------------------------------------------
2312 62 Karek Petr
2300 24 Petrek Ondej
2277 33 asn Iveta
2239 50 Hoznour Ren
2215 22 Lapa Vilm
Zpracujeme ho nsledovn:
const zdroj = 'test.txt';
      max = 50;
type sachista = record
     elo:integer;       {pro ty, co nevedi, ELO je vykonnost sachisty}
     vek:byte;
     jmeno:string[30];
     end;
var f:text;      {chceme-li pracovat se soubory jako textovymi, pouzijeme typ TEXT}
    s:string;
    p,q:integer;
    u:char;
    hrac:array[1..max] of sachista;
begin
p:=0;
Assign(f,zdroj);
Reset(f);
readln(f);         {preskocim prvni dve radky}
readln(f);
while not Eof(f) do     {dokud nejsme na konci souboru...}
   begin
   inc(p);
   Read(f,hrac[p].elo);     {nechceme nacist celou radku, ale jen prvni cislo}
   Read(f,hrac[p].vek);     {proto Read, ne Readln}
   Read(f,u);                  {nacteme "prebytecny" znak - oddelovaci mezeru}
   Readln(f,hrac[p].jmeno);    {a cele to ukoncime nactenim jmena}
   end;
Close(f);          {uzavru zdrojovy soubor}
writeln('Na soupisce je ',p,' hracu:');
for q:=1 to p do
    writeln(hrac[q].jmeno,#9,hrac[q].vek,#9,hrac[q].elo);
readln;
end.
Vtina programtor by ale tuto lohu eila tak, e by naetli celou dku a pak by ji rozebrali etzcovmi funkcemi. Postupn ten m toti zvan omezen:
1) je-li za etzcovm typem slo, obvykle se ji nenate, nebo je povaovno za soust etzce. To se d obejt jenom explicitnm deklarovnm dlky etzce.
2) sla v souboru mus bt od sebe oddlena mezerami. Lhostejno, jestli jednou nebo vce. (nen mon toto: 2312, 62, Karek Petr toto ale mon je: 2312 62 Karek Petr)
3) kdy je poruen bod 2, tak program skon s chybovou hlkou 106

Binrn soubory

Binrn soubory se rozdluj na typov a netypov. Typov soubory maj pedepsanou a rigidn vnitn strukturu a pracuje se s nimi pomoc Read/Write. Netypov jsou prost hromada bajt a pracuje se s nimi pomoc BlockRead/BlockWrite.
Pro problm koprovn souboru se vce hod netypov soubory, protoe ns nezajm, jak data koprujeme:
const zdroj = 'test.exe';
      cil   = 'test.ex~';
      delka_kusu = 65535;
type sachista = record
     elo:integer;       {pro ty, co nevedi, ELO je vykonnost sachisty}
     vek:byte;
     jmeno:string[30];
     end;
var f,g:file;      {netypove soubory jsou typu FILE}
                   {(typove by bylo FILE of <neco>}
                   {napr. file of sachista)}
    p,i:word;
    l:longint;
    buffer:array[1..delka_kusu] of byte;
begin
Assign(f,zdroj);
Assign(g,cil);
Reset(f,1);    {u netypovych souboru urcim delku bloku. Prakticky vzdy 1 bajt}
l:=FileSize(f);
Rewrite(g,1);
p:=0;
while not Eof(f) do     {dokud nejsme na konci souboru...}
   begin
   inc(p);
   BlockRead(f,buffer,delka_kusu,i); {zkusim nacist DELKA_KUSU bajtu, ale je}
                                     {mozne, ze se nacte mene, protoze soubor}
                                     {je kratsi nebo uz jsme neco nacetli predtim}
                           {pocet skutecne nactenych bajtu se nastesti ulozi do I}
   BlockWrite(g,buffer,i); {...a techto I bajtu zapisu do druheho souboru}
   end;
Close(f);
Close(g);
writeln('Soubor je velky ',l,' bajtu.');
writeln('Zkopiroval jsem ho v ',p,' krocich.');
readln;
end.
Tato metoda koprovn m vhodu, e doke bez pokozen zkoprovat jakkoliv soubory - neplat tu omezen o maximln dlce dku. Je ale teba dvat pozor na omezen Turbo pascalu, na maximln velikost promnn 64KB. Nkdy to nen problm (teba pi koprovn soubor), jindy je to obtnj, teba kdy natme obrzek. Je teba stle hldat dekompriman rutinu, aby nedola na konec zdrojovho bufferu a prbn "dotat" dal data.

Typov binrn soubory
eknu to takhle: j jsem je jet nikdy nepouil. Jejich problm je ten, e se pedpokld, e v celm souboru maj data jednotn tvar. V praxi je ale obvyklej, e soubor obsahuje hlaviku, nkolik podhlaviek a bloky dat. Nicmn, pokud bychom se vrtili k pkladu s achisty, tak zde by se typov soubor dal pout dobe.
const soubor = 'test.txt';
type sachista = record
     elo:integer;       {pro ty, co nevedi, ELO je vykonnost sachisty}
     vek:byte;
     jmeno:string[20];
     end;
const soupiska1:array[1..5] of sachista =
      ((elo:2113; vek:35; jmeno:'Mrzek Jan'),
       (elo:2104; vek:56; jmeno:'Kepelka Vclav'),
       (elo:2088; vek:24; jmeno:'Stblov Klra'),
       (elo:2039; vek:40; jmeno:'Pichour Otakar'),
       (elo:1992; vek:25; jmeno:'arfov Ilona'));

var f,g:file of sachista;
    soupiska2:array[1..5] of sachista;
    i:word;
begin
{Takhle soupisku ulozime}
Assign(f,soubor);
Rewrite(f);      {opet neuvadim velikost bloku}
for i:=1 to 5 do Write(f,soupiska1[i]);
Close(f);

{A takhle nacteme a zobrazime}
Assign(g,soubor);
Reset(g);
for i:=1 to 5 do
    begin
    Read(g,soupiska2[i]);
    writeln(soupiska2[i].jmeno,#9,soupiska2[i].vek,'   ',soupiska2[i].elo);
    end;
Close(g);
readln;
end.

Zpracovn chyb

Nene, nemm na mysli, e m teni jsou lemra, kte nedokou napsat nic sprvn. Chyby v souvislosti se soubory nesouvis s kvalitou kdu. Mm na mysli chybov stavy, kter vzniknou pi nedostatku msta na disku pi ukldn, pi neexistenci souboru pi natn a podobn. Z hlediska programtora tedy nepedvdateln chyby.
Chovn pascalu pi vzniku takovto bhov chyby souvis s nastavenm direktivy $I (a ekvivalentn poloky v nastaven). Pokud je nastaveno $I+, tak se pi vzniku chyby prost ukon a vype chybovou hlku. Prost, funkn, ale nepsob to zrovna profesionln. Jestlie si chceme takov situace zpracovat sami, musme se pepnout do reimu $I-.
V knkch o programovn se vtinou doporuuje dvat $I- jenom k operacm vstupu/vstupu a jinak bt ve zbytku programu v reimu $I+
Tedy takto:
...
Assign(f,'c:\pascal\soubor.dat');
{$I-}
Reset(f,1);
{$I+}
n:=IOresult;
if n<>0 then
   begin
   if n=2 then writeln('Soubor neexistuje (ale cesta jo)') else
   if n=3 then writeln('Neexistujc cesta') else
   if n=4 then writeln('Prilis mnoho poskrabanych souboru') else
      writeln('Nejaka chyba');
   Exit;
   end;
{$I-}
BlockRead(f,buffer,sizeof(buffer),i);
{$I+}
n:=IOresult;
if n<>0 then
   begin
   if n=100 then writeln('Chyba pri cteni z disku (poskrabane CD?)') else
      writeln('Nejaka chyba');
   Exit;
   end;
Podle m je neustl pepnn $I+/- blbost. Lep mi pjde dt prost na zatek programu $I- a dl u s tm nearovat. Ovem pozor! Jestlie se v $I- reimu vyskytne chyba, jsou vechny vstupn/vstupn operace zablokovny do t doby, ne zavolte funkci IOresult. IOresult toti nen promnn, je to funkce, kter krom toho, e vrac kd chyby jet odblokuje vnitn pojistku a umon tak znovu pracovat se soubory. Jestlie se tedy vyskytne njak chyba a vy ji pomoc IOresult nezpracujete, tak v program pestane pracovat se soubory na disku a vy si toho nevimnete.
Nejastjm "mimodnm stavem" bv neexistence souboru. Funkce IOresult to odhal a zptn, lep je to ale eit v pedstihu. Jestlie pracujeme ve Freepascalu, tak je nejjednodu pout funkci FileExists z jednotky SysUtils.
V Turbo pascalu si to musme zjistit sami:
Function ExistFile(s:string):boolean;
{Zjisti,zda dany soubor existuje }
{potrebuje unit DOS}
var r:searchrec;
begin
if s='' then begin ExistFile:=false;Exit;end;
FindFirst(s,archive+hidden+readonly+sysfile,r);
ExistFile:=DosError=0;
end;
Tento kd bude samozejm fungovat i ve Freepascalu.

Dleit me bt i zjitn existence adrese. Tady si to musme zjistit sami v kadm ppad, protoe jednotka SysUtils takovou funkci postrd:
Function ExistDir(s:string):boolean;
{Zjisti,zda dany adresar existuje }
{potrebuje unit DOS}
var r:searchrec;
    a:byte;
begin
if s='' then begin ExistDir:=false;Exit;end;
if Copy(s,a,1)='\' then dec(s[0]);
r.attr:=0;
FindFirst(s,directory,r);
if DosError=0 then
   begin
   if ExistFile(s+'\nul') then ExistDir:=true else ExistDir:=false;
   end else ExistDir:=false;
end;
Posledn vc, kterou bych chtl v tomto oddle zmnit, je voln funkce FindFirst. I kdy hledte jenom adrese, tak doporuuju brt v prvn chvli vechno a vstup filtrovat a potom. Do parametru atributy ale v dnm ppad nedvejte konstantu AnyFile, jinak budete dostvat prapodivn chyby, kter jsou o to zludnj, e se objevuj jen na nkterch potach (systmech) a na jinch ne.
Pouvejte proto tohle:
Procedure Nacti_soubory_a_ne_adresare(adresar:string);
var r:registers;
if maska='' then maska:='*.*';
findfirst(adresar+'*.*',readonly+directory+sysfile+archive,r);
while doserror=0 do{dokud je neco nalezeno...}
   begin
   if (r.attr and directory)=0 then{...a neni to adresar (ma se delat seznam souboru, ne adresaru)}      
      ZpracujSoubor(r.name);
   findnext(r);
   end;

Objektov prostedky

Prce se soubory pomoc objekt z rodiny TStream se podob netypovm binrnm souborm. Nicmn umouje zapisovat i textov etzce. Objekt TStream a jeho potomci jsou v jednotce Objects.
Objektov prce se soubory m nkolik obrovskch vhod, kter plynou z polymorfizmu potomk objektu TStream.
Potomek TMemoryStream te a zapisuje nikoliv do souboru, ale do bufferu v operan pamti. Ideln pro doasn ukldn jakchkoli dat - nen teba se babrat s buffery, poli, spojovmi seznamy, ve jde samo.
TDosStream odpovd klasick cest pomoc Assign, Reset a spol.
TBufStream je jako TDosStream, ale V/V operace jsou bufferovan a tud rychlej.
Turbo pascal m jet TEmsStream, kter se podob TMemoryStreamu, ovem pracuje s pamt EMS.
Na internetu se daj sehnat jet dal rozen, napklad zde, kter pidvaj dal potomky, nap. TXMSstream a jin.
Podvejme se na streamy prakticky. Zase vm naped uku, jak zkoprovat soubor:
uses objects;
const SOUBOR='soubor.dat';
      NOVYNAZEV='soubor.new';

var f,g:TDOSstream;
begin
f.init(SOUBOR,stOpenRead);      {pristup jen pro cteni}
g.init(NOVYNAZEV,stCreate);     {vytvori novy soubor}
g.CopyFrom(f,f.GetSize);
f.Done;
g.Done;
writeln('Soubor ',SOUBOR,' byl zkopirovan do ',NOVYNAZEV,'.');
readln;
end.
Streamy maj geniln metodu CopyFrom, kter ve vznamn uleh. V nsledujcm pkladu budeme soubor nejenom koprovat, ale i kdovat, take ns ek vce prce:
uses objects;
{$R-,$O-}                  {pri kodovani nechci dostavat priblble chyby o}
                           {preteceni/podteceni bajtu}
const SOUBOR='soubor.dat';
      NOVYNAZEV='soubor.new';
      VELBUF = 8192;
      KOD:longint = 1;

var f,g:TDOSstream;
    buffer:array[1..VELBUF] of byte;
    n,i:longint;
begin
f.init(SOUBOR,stOpenRead);      {pristup jen pro cteni}
g.init(NOVYNAZEV,stCreate);     {vytvori novy soubor}
repeat
n:=f.GetPos;               {zaznamenam pozici ve streamu pred ctenim}
f.Read(buffer,VELBUF);        {pokusim se precist VELBUF bajtu}
if f.status=stReadError then  {nedoslo ke cteni za koncem souboru?}
   begin
   f.reset;                {resetuj chybovy stav}
   n:=f.GetSize-n;         {a zjisti, jak velky byl tento posledni usek}
   f.Read(buffer,n);       {neni mi jasne, zda je to nutne. nic tim ale nezkazime}
   end
   else n:=f.GetPos-n;     {o kolik bajtu jsme se soupli?}

for i:=1 to n do
    inc(buffer[i],KOD);    {zakoduju}

g.Write(buffer,n);         {a zapisu}
until n<>VELBUF;           {opakuj, dokud jsme nezpracovali cely soubor}

f.Done;
g.Done;
writeln('Soubor ',SOUBOR,' zakodovan a ulozen do ',NOVYNAZEV,'.');
readln;
end.
Vidte, e metoda TStream.Write je podobn procedue BlockWrite a TStream.Read procedue BlockRead. Vidte ale, e metoda Read postrd parametr, kolik bajt se doopravdy peetlo. Proto si to musme sami, pomrn tkopdn, hldat.
Toto povdn o streamech berte jen jako takov nakopnut podvat se pro dal informace do manulu. Stejn jako klasick prostedky m sv analogy funkc jako Seek a Truncate apod. Streamy zskaj jet dal slu ve spojen s potomky typu TObject. Ukldn poloek objekt, ale i zznam me bt pomrn pracn zaleitost, protoe leckdy je poteba ukldat poloku po poloce. Streamy maj ale mechanismus, jak napsat univerzln metody pro ukldn a natn poloek objekt. Pro dan objekt napete dan procedury, registrujete je a pomoc metod TStream.Get a TStream.Get je mete natat, ani byste museli stream informovat o pesnm formtu dat. Znovu odkazuji na manul.
Vechny tyto vci funguj i v TP i ve FP. FreePascal um krom objekt pouvat i tdy. Tdy TP nezn, objevuj se a v Delphi, kter naopak neznaj objekty. FreePascal zn oboje.
Takto bude vypadat pedchoz loha pomoc nikoliv objektu TStream, ale tdy TStream:
uses Classes;
{$R-,$O-}                  {pri kodovani nechci dostavat priblble chyby o}
                           {preteceni/podteceni bajtu}
const SOUBOR='soubor.dat';
      NOVYNAZEV='soubor.new';
      VELBUF = 8192;
      KOD:longint = 1;

var f,g:TFileStream;
    buffer:array[1..VELBUF] of byte;
    n,i:longint;
begin
f:=TFileStream.Create(SOUBOR,fmOpenRead);      {pristup jen pro cteni}
g:=TFileStream.Create(NOVYNAZEV,fmCreate);     {vytvori novy soubor}
repeat
n:=f.Read(buffer,VELBUF);
for i:=1 to n do
    inc(buffer[i],KOD);    {zakoduju}

g.Write(buffer,n);         {a zapisu}
until n<>VELBUF;           {opakuj, dokud jsme nezpracovali cely soubor}

f.Destroy;
g.Destroy;
writeln('Soubor ',SOUBOR,' zakodovan a ulozen do ',NOVYNAZEV,'.');
readln;
end.
2007-06-07 | Laaca