Práca so súbormi. Konzolová aplikácia.

14.02.2010 21:44

Práca so súbormi

Konzolová aplikácia

Vývojové prostredie Delphi umožňuje vytvárať aj aplikácie, ktoré nevyužívajú Windows formuláre a komponenty, ale pracujú v textovom okne. Takáto aplikácia nemá žiadne tlačidlá ani grafickú plochu a nereaguje ani klikanie  myši. Celý program sa redukuje na spustenie príkazov medzi begin a end "hlavného programu":

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

begin
 { telo programu }
end.

Premenné a pomocné procedúry definujeme tesne pred begin.

Z historického hľadiska je tento spôsob najstarší a takto sa programovalo už v polovici minulého storočia. Ale v niektorých situáciách toto využívajú niektorí programátori aj v súčasnosti.

Vypisovanie v konzolovej aplikácii

Na výpis ľubovoľných hodnôt (textov a čísel) do textového okna konzolovej aplikácie slúžia príkazy Write a Writeln. Oba tieto príkazy môžu mať ľubovoľný počet parametrov oddelených čiarkami - to sú hodnoty a znakové reťazce, ktoré budú postupne vypisované do textového okna.
Príkaz Writeln sa od Write líši len tým, že po vypísaní všetkých parametrov kurzor prejde na začiatok nasledovného riadka a teda všetky ďalšie výpisy sú už v ďalšom riadku. Syntax oboch príkazov je

Write(hodnota1, hodnota2, ..., hodnotaN);
Writeln(hodnota1, hodnota2, ..., hodnotaN);

pričom príkaz Writeln sa môže použiť aj bez parametrov - v tomto prípade sa nevypíše nič, len sa kurzor nastaví na začiatok ďalšieho riadka. Samotné hodnoty (texty aj čísla) sa najprv vypočítajú (vyhodnotia) a potom sa vypíšu v textovom tvare - čísla sa nevypisujú so žiadnymi medzerami, preto ich často pri výpise oddeľujeme medzerami, napr.

  Writeln('*** prvý riadok obsahuje len nejaký nadpis ***');
  Writeln;                                                                   // ďalší riadok je prázdny
  Write('desiata mocnina 2 je ');
  Writeln(2*2*2*2*2*2*2*2*2*2);
  A := 37;
  B := 14;
  Wtiteln('výpočet ', A, ' * ', B, ' = ', A*B);
  Writeln(A, B, A*B);                                                  // tieto čísla nebudú oddelené medzerou, preto sa vypíše: 3714518

Podobne treba myslieť na medzery medzi číslami, aj pri výpise for-cyklom:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  I: Integer;

begin
  for I := 1 to 100 do
  begin
    Write(I*I*I);
    if I mod 8 = 0 then
      Writeln
    else
      Write(' ');
  end;
  Readln;
end.

Program vypisuje tretie mocniny čísel, pričom po každom ôsmom čísle prejde výpis do nového riadka. Ak toto spustíme ako konzolovú aplikáciu, tak program hneď po výpise všetkých čísel končí a teda automaticky zatvorí textové okno aplikácie. Z tohto dôvodu dáme na koniec programu príkaz Readln, ktorý pozdrží ukončenie programu - program teraz čaká na stlačenie klávesu <Enter> a až potom skončí.

Príkazy Write a Writeln umožňujú výpisy aj jednoducho naformátovať - ku každej vypisovanej hodnote môžeme zapísať aj formátovací parameter:
je to celé číslo za znakom ':' a označuje "šírku vypisovaného poľa":

  • ak má vypisovaná hodnota (text alebo číslo) menej znakov ako "šírka poľa", výpis sa zľava doplní medzerami,
  • ak má vypisovaná hodnota viac znakov ako "šírka poľa", tento formátovací parameter sa ignoruje a vypíše sa kompletná hodnota.

Napr. tento program bez formátovacích parametrov:

begin
  Writeln('Bratislava', 452000);
  Writeln('Kosice', 241000);
  Writeln('Presov', 93000);
  Writeln('Nitra', 88000);
  Writeln('Zilina', 87000);
  Writeln('Banska Bystrica', 85000);
  Readln;
end.

vypíše:

Bratislava452000
Kosice241000
Presov93000
Nitra88000
Zilina87000
Banska Bystrica85000

Ale s formátovacími parametrami:

begin
  Writeln('Bratislava':20, 452000:8);
  Writeln('Kosice':20, 241000:8);
  Writeln('Presov':20, 93000:8);
  Writeln('Nitra':20, 88000:8);
  Writeln('Zilina':20, 87000:8);
  Writeln('Banska Bystrica':20, 85000:8);
  Readln;
end.

vypíše:

Bratislava 452000
Kosice  241000
Presov     93000
Nitra     88000
Zilina     87000
Banska Bystrica 85000

Čítanie v konzolovej aplikácii

Príkaz Readln môžeme použiť nielen na pozdržanie výpisu, resp. programu, ale aj na načítanie údajov od používateľa - používateľ napíše nejaké číslo, resp. čísla oddelené medzerami a Read, resp. Readln ich prečíta. Syntax príkazov je:

Readln;
Readln(premenná);
Readln(premenná1, premenná2, ...);
Read(premenná);
Read(premenná1, premenná2, ...);

Readln sa od Read líši len tým, že po prečítaní údajov do premenných, príkaz Readln ignoruje zvyšok vstupného riadka a ďalší Read, resp. Readln bude opäť očakávať od používateľa zadanie nejakých ďalších hodnôt. Príkaz Read, na rozdiel od Readln sa nesmie použiť bez aspoň jedného parametra. Tiež si uvedomte, že
Readln(A, B, C);
je to isté ako postupné volania:
Read(A); Read(B); Read(C); Readln;
Nasledujúci program ilustruje načítanie a spracovanie troch čísel:

var
  A, B, C: Integer;
begin
  Write('Zadaj velkost kvadra: ');
  Readln(A, B, C);
  Writeln('Kvader ma hrany: ', A, ' ', B, ' ', C);
  Writeln('Povrch = ', 2*(A*B + A*C + B*C));
  Writeln('Objem = ', A * B * C);
  Readln;
end.

a výstup je napr.

Zadaj velkost kvadra: 4 5 6
Kvader ma hrany: 4 5 6
Povrch = 148
Objem = 120

Ak sme sa pri zadávaní čísel pomýlili a na vstupe sú aj iné znaky ako čísla a medzery, program spadne s chybovým hlásením  "Invalid numeric input".
Nasledujúci program vypisuje ordinálne hodnoty niektorých písmen:

var
  Znak: Char;
begin
  for Znak := 'A' to 'L' do
    Writeln('znak "', Znak, '" ma ordinalnu hodnotu ', Ord(Znak));
  Readln;
end.

a vypíše

znak "A" ma ordinalnu hodnotu 65
znak "B" ma ordinalnu hodnotu 66
znak "C" ma ordinalnu hodnotu 67
znak "D" ma ordinalnu hodnotu 68
znak "E" ma ordinalnu hodnotu 69
znak "F" ma ordinalnu hodnotu 70
znak "G" ma ordinalnu hodnotu 71
znak "H" ma ordinalnu hodnotu 72
znak "I" ma ordinalnu hodnotu 73
znak "J" ma ordinalnu hodnotu 74
znak "K" ma ordinalnu hodnotu 75
znak "L" ma ordinalnu hodnotu 76

Pri čítaní do znakových premenných často využívame while-cyklus. Program prečíta meno a priezvisko a potom vypíše iniciálky:

var
  Znak1, Znak2: Char;

begin
  Write('Zadaj meno a priezvisko: ');
  Read(Znak1, Znak2);
  while Znak2 <> ' ' do
    Read(Znak2);
  Readln(Znak2);
  Writeln('Tvoje inicialky su: ', UpCase(Znak1), UpCase(Znak2));
  Readln;
end.

a výstup napr.

Zadaj meno a priezvisko: Harry potter
Tvoje inicialky su: HP

Načítavanie znakov môžeme použiť aj na prečítanie celočíslenej hodnoty: postupne budeme čítať po jednom znaku a kým sú to číslice, budeme z toho skladať výsledné číslo:

var
  Znak: Char;
  Cislo: Integer;
begin
  Write('Zadaj cislo: ');
  Cislo := 0;
  Read(Znak);
  while (Znak >= '0') and (Znak <= '9') do
  begin
    Cislo := 10 * Cislo + Ord(Znak) - Ord('0');
    Read(Znak);
  end;
  Readln;
  Writeln('Precitane cislo: ', Cislo);
  Writeln('Druha mocnina cisla = ', Sqr(Cislo));
  Writeln('Stvrta mocnina cisla = ', Sqr(Sqr(Cislo)));
  Readln;
end.

a výstup napr.

Zadaj cislo: 213x
Precitane cislo: 213
Druha mocnina cisla = 45369
Stvrta mocnina cisla = 2058346161

Poznámky:

  • tento algoritmus by sa dal použiť aj na čítanie čísel v iných číselných sústavách - nahraďte konštantu 10 napr. 8 a samozrejme aj logická podmienka v cykle while potom musí mať inú maximálnu cifru (Znak <= '7')

Súbory 

Pojem súbor označuje časť diskového priestoru, ktorý obsahuje určité údaje. Bez ohľadu na to  na akom zariadení sú dáta uložené sa v prostredí programovacieho jazyka pristupuje k súborom rovnakým spôsobom. Vlastné vykonanie akcií so súborom zabezpečuje  použitý operačný systém,  preto pri písaní programu sa nemusíme zaoberať vlastnosťami nosiča údajov.


Každý súbor je v rámci diskového priestoru označený názvom a prístupovou cestou. Tieto dva údaje zaisťujú jednoznačnosť označenia súboru.
Z hľadiska spracovania súborov, môžeme súbory rozdeliť podľa rôznych kritérií.

1.  Podľa použitia riadiacich znakov:
  • súbory textové
  • súbory netextové s udaným typom 
  • súbory netextové bez udania typu 
2.  Podľa druhu práce so súbormi:
  • súbory určené len pre čítanie
  • súbory určené len pre zápis
  • súbory určené pre čítanie aj zápis 
3.  Podľa spôsobu spracovania údajov v súbore:
  • súbory spracovávané postupne (sekvenčne)
  • súbory s priamym prístupom

Toto rozdelenie je všeobecné a nezáleží na použitom programovacom jazyku. Fyzická podoba súboru je vždy rovnaká. Uvedené rozdelenie sa týka výhradne našej práce so súborom, nášho logického prístupu k súboru. 

Textové a netextové súbory.

Textové súbory 

Ak sa rozhodneme považovať súbor za textový, znamená to, že informácie uložené v ňom sú chápané nasledovne:
- znaky, ktorých ordinálna hodnota je menšia ako 32, sú chápané ako riadiace znaky, t.j. vykoná sa dopredu dohodnutá akcia (nemusia sa objaviť na obrazovke)

BEL  bell  zvonček
BS  backspace  späť
TAB  tabulator  tabulátor
10  LF  line feed  riadkový posun
12  FF  form feed  stránkový posun
13  CR  carriage return  návrat vozíka (kurzora, hlavy tlačiarne atd.)
26  EOF  end of file  koniec súboru
27  ESC  escape  únik

- znaky, ktorých ordinárna hodnota je 32 a viac, sú nositeľmi textovej informácie.

Textový súbor  je postupnosť znakov členených do riadkov, ktorých dĺžka nemusí  byť rovnaká. Je to špeciálny typ súboru, kde je riadok základnou jednotkou. Jednotlivé riadky sú ukončené dvojicou znakov  Cr  Lf . (CR - Carriage Return – s ASCII hodnotou 13 a LF – Line Feed – s ASCII hodnotou 10. Z historických dôvodov CR je návrat vozíčka na začiatok tlač. zariadenia a LF je posun papiera o riadok). 
Celý súbor je ukončený znakom   EOF End of File.
Súbor  je otvorený buď pre   čítanie  alebo  zápis. 
Textové  súbory  môžeme  použiť    aj  na  prácu  s  číslami,  nielen  s textovými  údajmi. Ak textový súbor obsahuje  čísla, môžeme z riadkov  čítať  údaje  do číselnej premennej. Štruktúru textového súboru si môžeme predstaviť  podľa schémy: 
 

Pre typové súbory ponúka jazyk viac príkazov pre prehľadávanie a zmeny v súbore, ale často  pre jednoduché úkony so súborom si vystačíme aj s textovými súbormi. 

Netextové súbory sú opakom textových súborov, pri spracovaní netextového súboru sa znaky s ordinálnou hodnotou menšou ako 32 nepovažujú za znaky riadiace, ale spracujú sa ako akékoľvek iné údaje.
Ak sú údaje v súbore všetky rovnakého typu, napr. len čísla real, len znaky, alebo len záznamy atd., potom hovoríme o netextových súboroch s udaným dátovým typom. Netextové súbory bez udaného dátového typu môžu obsahovať údaje rôznych dátových typov, t.j. zmes čísiel, znakov, záznamov, množín, reťazcov atd. V tomto prípade musí byť známe, aké druhy údajov a v akom poradí sa majú v súbore objaviť.

Typové súbory 

Prvkami typových súborov sú  premenné  štandardného  typu  alebo  štruktúrované  premenné typu záznam. Každý údaj v súbore má rovnakú veľkosť,  určenú  svojím typom, položky súboru sú očíslované od 0, čo umožňuje nesekvenčné prehľadávanie súboru – môžeme skočiť na položku s istým  číslom. Vieme zistiť aj počet položiek súboru.  Na konci súboru je znak konca súboru – EOF (End of file -  znak s ASCII hodnotou 26). Štruktúru typového súboru si môžeme predstaviť  podľa schémy:

Netypové súbory 

Súbory bez typu  predstavujú vstupno-výstupné kanály nízkej úrovne, používané pre  priamy prístup na disk, bez rozlíšenia typov a štruktúr prenášaných položiek. Dovoľujú použitie všetkých štandardných procedúr používaných s typovými súbormi, s výnimkou procedúr Read a Write. Namiesto nich sa pre rýchly prenos dát používajú procedúry BlockRead  a BlockWrite.

Otvorenie súboru  je  činnosť,  po  ktorej  úspešnom  vykonaní  nám  operačný  systém  umožní  ďalšiu prácu so súborom a jeho obsahom. Pri otváraní súboru sa okrem iného stanoví aj to, či je súbor určený pre čítanie, pre zápis, alebo pre oboje.

Zatvorenie súboru  je činnosť, ktorou operačnému systému oznamujeme, že súbor sa už nebude používať. 

Otvorenie a zatvorenie súboru vykonávame s každým súborom bez ohľadu na to či je textový, alebo netextový, určený na čítanie, alebo zápis, pre oboje. 

S textovými súbormi zaobchádzame vždy len ako so súbormi pre čítanie alebo pre zápis, nie pre čítanie aj zápis. S netextovými súbormi môžeme pracovať všetkými tromi spôsobmi. 

Postupné spracovanie dát znamená, že dáta sú zo súboru čítané postupne v tom poradí ako boli zapísané. Pri čítaní nie je možné sa vrátiť ani skočiť inde. Postupné spracovávanie sa používa u textových súborov (u nich  je  to  jediná  možnosť),  niekedy  aj  u ostatných  typov súborov. 

Priamy prístup k údajom v súbore  znamená,  že každá položka  súboru má  svoje poradové číslo. Toto číslo môžeme použiť pre skok na určitú položku. Túto metódu používame pri práci s netextovými súbormi.

Textové súbory TextFile

Textové súbory v pascale majú iba sekvenčný prístup a preto

  • sa musí rozlišovať, či zo súboru údaje čítame alebo ich do neho zapisujeme:
    • vstupný súbor - môžeme len čítať pripravené údaje
    • výstupný súbor - môžeme len zapisovať nové údaje na koniec súboru
  • do textového súboru sa nedá aj zapisovať aj súčasne z neho čítať

Ako pracujeme so súborom:

1. najprv zadeklarujeme premennú typu TextFile, pomocou ktorej budeme k tomuto súboru ďalej v programe pristupovať

  • var Subor: TextFile;

2. ďalej treba tejto "súborovej" premennej priradiť nejaký konkrétny súbor (najčastejšie na disku):

  • AssignFile(Subor, meno_súboru);

3. potom treba súbor "otvoriť" - pri otváraní určíme, či z neho budeme len čítať (Reset) alebo budeme do neho len zapisovať (Rewrite):

  • Reset(Subor); // otvorenie súboru na čítanie - súbor už musí existovať
  • Rewrite(Subor); // otvorenie súboru na zápis - ak už existuje, tak sa najprv vyprázdni

4. potom môžeme pracovať s jeho obsahom:

  • Read(Subor, premenná); // čítanie zo súboru - z pozície ukazovateľa
  • Readln(Subor, ...);
  • Write(Subor, hodnota); // zápis do súboru - na jeho koniec
  • Writeln(Subor, ...);

5. na záver ho treba "zatvoriť", t.j. ukončíme s ním ďalšiu prácu:

  • CloseFile(Subor);

Príkazy Read a Write sú veľmi podobné tým, ktoré sme používali v konzolových aplikáciách - aj pravidlá sú tu veľmi podobné. Najväčší rozdiel je ale v tom, že pri práci so súbormi musíme počítať s tým, že súbor sa skladá z viacerých riadkov a tieto sú oddelené špeciálnou značkou <Eoln>.
Zavedieme pojem ukazovateľ, t.j. pozícia v súbore:

  • na začiatku (pri otvorení súboru) je na 1. znaku
  • po každom Read aj Write sa automaticky posunie o 1 znak vpravo (resp. o toľko znakov, koľko spracoval)
  • pri práci so súborom sa môžeme hocikedy nastaviť na jeho začiatok: príkaz Reset umožní čítanie, príkaz Rewrite vymaže momentálny obsah a teda môžeme začať zapisovať od začiatku
Koniec súboru a koniec riadka

Na testovanie konca súboru slúži štandardná logická funkcia Eof(Subor) - skratka z end of file

  • vráti True, ak je ukazovateľ nastavený za posledným znakom súboru
  •  najčastejšie sa bude používať v teste while not Eof(Subor) do ..., t.j. rob, kým nie je koniec súboru
  • čítanie za koncom súboru najčastejšie spôsobí chybovú správu
  • uvedomte si, že ak je súbor otvorený na zápis (Rewrite), tak ukazovaťeľ je stále na konci a teda Eof stále vracia True
  • znak #26 má v pascale (z historických dôvodov) niekedy špeciálny význam: čítanie textového súboru si na ňom "myslí", že je na konci súboru (Eof(Subor)=True) a nedovolí čítať ďalšie znaky za ním

Na testovanie konca riadka slúži štandardná logická funkcia Eoln(Subor) - skratka z end of line

  • vráti True, ak je ukazovateľ v súbore na značke <Eoln>
  • True vráti aj vtedy, keď je za posledným znakom súboru, t.j. ak platí Eof(Subor)=True, tak platí aj Eoln(Subor)=True
  • značka <Eoln> sa vnútorne kóduje dvomi znakmi #13 a #10 (hovoríme im CR a LF)
  • príkazom Readln(Subor) preskočíme od momentálnej pozície ukazovateľa všetky znaky až za najbližšiu značku <Eoln> (na konci súboru nerobí nič)
  • POZOR, pomocou Read(Subor, Znak) je možné čítať aj značku <Eoln>, lenže táto sa potom chápe ako 2 znaky #13 a #10 a nie ako nejaký jeden špeciálny znak
  • príkaz Readln(Subor, Znak); je skrátený tvar pre dvojicu príkazov Read(Subor, Znak); Readln(Subor);
  • príkaz Writeln(Subor); sa dá zapísať aj ako Write(Subor, #13#10);

V nasledujúcom príklade zistíme počet medzier v textovom súbore "text.txt":

var
  Subor: TextFile;
  Znak: Char;
  Pocet: Integer;
begin
  AssignFile(Subor, 'medzery.txt');
  Reset(Subor);
  Pocet := 0;
  while not Eof(Subor) do
  begin
    Read(Subor, Znak);
    if Znak = ' ' then
      Inc(Pocet);
  end;
  CloseFile(Subor);
  // výsledok vypíšeme do textovej plochy:
  Memo1.Lines.Append('Počet medzier v súbore ' + IntToStr(Pocet));
end;

Poznámka

  • všimnite si, že sme si tu vôbec nevšímali konce riadkov, t.j. značky <Eoln> - tie sme čítali ako dva obyčajné znaky

Zistíme, počet riadkov textového súboru "text.txt":

var
  Subor: TextFile;
  Pocet: Integer;
begin
  AssignFile(Subor, 'text.txt');
  Reset(Subor);
  Pocet := 0;
  while not Eof(Subor) do
  begin
    Readln(Subor);
    Inc(Pocet);
  end;
  CloseFile(Subor);
  Memo1.Lines.Append('Počet riadkov v súbore ' + IntToStr(Pocet));
end;

Poznámka

  • tu sme si vôbec nevšímali obsah riadkov - príkazom Readln sme preskočili kompletný obsah riadka až za najbližšiu značku <Eoln> - zaujímali nás tu len počty riadkov

Zistíme, dĺžku najdlhšieho riadka súboru "text.txt":

var
  Subor: TextFile;
  Znak: Char;
  Max, Dlzka: Integer;
begin
  AssignFile(Subor, 'text.txt');
  Reset(Subor);
  Max := 0;
  while not Eof(Subor) do
  begin
    Dlzka := 0;
    while not Eoln(Subor) do    // zistí dĺžku riadka
    begin
      Read(Subor, Znak);
      Inc(Dlzka);
    end;
    Readln(Subor);             // tu nesmieme zabudnúť na Readln
    if Dlzka > Max then
      Max := Dlzka;
  end;
  CloseFile(Subor);
  Memo1.Lines.Append('Dĺžka najdlhšieho riadka ' + IntToStr(Max));
end;

Poznámka:

  • tieto tri rôzne programy ilustrujú tri najčastejšie schémy spracovania textového súboru:
    • čítanie po znakoch - ignorujeme značky <Eoln>
    • čítanie celých riadkov
    • čítanie po riadkoch - v každom riadku čítame po znakoch až do konca riadka

Veľmi často sa spracovanie textového súboru zapisuje aj takto:

var
  Subor: TextFile;
  Znak: Char;
begin
  AssignFile(Subor, 'text.txt');
  Reset(Subor);
  while not Eof(Subor) do
    if Eoln(Subor) then     // ak je koniec riadka
    begin
      Readln(Subor);
      // spracuj koniec riadka
    end
    else             // ak nie je koniec riadka, spracujem jeden znak
    begin
      Read(Subor, Znak);
      // spracuj Znak
    end;
  end;
  CloseFile(Subor);
end;

Zápis do súboru

Do súboru zapisujeme pomocou príkazov Write a Writeln. Pred ich prvým použitím musí byť súbor otvorený na zápis pomocou
Rewrite(Subor);

Príkaz Rewrite má ale tieto dôsledky:

  • ak súbor už existoval, tak hneď po Rewrite sa jeho obsah zruší
  • ak súbor ešte neexistoval, vytvorí sa s prázdnym obsahom
  • ukazovateľ v súbore je vždy nastavený na jeho koniec

Príkazy Write a Writeln majú tieto ďalšie vlastnosti:

  • príkaz Writeln(Subor) zapíše do súboru značku <Eoln>, t.j. robí to isté ako Write(Subor, #13#10)
  • príkaz Writeln(Subor, hodnota) je skrátený tvar pre Write(Subor, hodnota); Writeln(Subor); alebo aj Write(Subor, hodnota, #13#10);

Vytvoríme súbor "text.txt" z písmen 'a' až 'z' a potom jeho obsah vypíšeme do textovej plochy:

procedure TForm1.Button1Click(Sender: TObject);
var
  Subor: TextFile;
  Znak, Znak1: Char;
begin
  AssignFile(Subor, 'text.txt');
  Rewrite(Subor);
  for Znak := 'a' to 'z' do
  begin
    for Znak1 := Znak to 'z' do
      Write(Subor, Znak1);
    Writeln(Subor);
  end;
  CloseFile(Subor);
  Memo1.Lines.LoadFromFile('text.txt');
end;

Poznámka
 

  • využili sme
    Memo1.Lines.LoadFromFile(meno_súboru)
    ktorý obsah celého práve vyrobeného súboru vypísal do textovej plochy

Vytvoríme kópiu súboru unit1.pas do súboru text.txt

var
  Subor1, Subor2: TextFile;
  Znak: Char;
begin
  AssignFile(Subor1, 'unit1.pas');
  Reset(Subor1);
  AssignFile(Subor2, 'text.txt');
  Rewrite(Subor2);
  while not Eof(Subor1) do
    if Eoln(Subor1) then
    begin
      Readln(Subor1);
      Writeln(Subor2);
    end
    else
    begin
      Read(Subor1, Znak);
      // spracuj prečítaný znak
      Write(Subor2, Znak);
    end;
  CloseFile(Subor1);
  CloseFile(Subor2);
  Memo1.Lines.LoadFromFile
('text.txt');
end;

iný variant kopírovania súboru - nevšímame si konce riadkov - pritom prerábame malé písmená na veľké:

var
  Subor1, Subor2: TextFile;
  Znak: Char;
begin
  AssignFile(Subor1, 'unit1.pas');
  Reset(Subor1);
  AssignFile(Subor2, 'text.txt');
  Rewrite(Subor2);
  while not Eof(Subor1) do
  begin
    Read(Subor1, Znak);
    if (Znak >= 'a') and (Znak <= 'z') then
      Znak := Char(Ord(z)-Ord('a')+Ord('A')); // alebo Dec(Znak, 32);
    Write(Subor2, Znak);
  end;
  CloseFile(Subor1);
  CloseFile(Subor2);
  Memo1.Lines.LoadFromFile('text.txt');
end;

Poznámka:
 

  • na prerábanie malých písmen na veľké sme mohli použiť aj štandardnú znakovú funkciu Upcase(Znak), t.j. namiesto príkazu if sme mohli zmeniť priamo príkaz Write:
    Write(Subor2, Upcase(Znak));

Ďalšie námety:

  • postupnosť za sebou idúcich medzier nahraď jednou medzerou
  • vyhoď riadky, ktoré sú prázdne alebo obsahujú len medzery
  • vyhoď medzery na konci (na začiatkoch) riadkov
  • postupnosť znakov 'end' nahraď '***'
Čítanie čísel

Už vieme, že pomocou Read môžeme čítať aj čísla (celé aj reálne), ale si treba zapamätať, že v súbore musia byť tieto čísla ukončené medzerou, koncom riadka alebo tabulátorom (znak s kódom #9). Ak Cislo je číselná premenná (Integer alebo Real), potom

Read(Subor, Cislo);

  • najprv preskočí všetky medzerové znaky (medzera, <Eoln> alebo #9)
  • potom ďalšie znaky zo vstupu prekonvertuje na číslo
  • ak je toto číslo ukončené nemedzerovým znakom (iným ako medzera, <Eoln> alebo #9, napr. ',' alebo ';'), tak vyhlási chybu Invalid numeric format
  • POZOR! čítanie čísla na konci súboru (t.j. platí Eof, ale aj ak sú tam len medzery) vráti hodnotu 0 - treba sa tohto vyvarovať!

Profesionálny softvér tento štandardný Read na čítanie čísel nepoužíva, lebo chyba v súbore spôsobí chybovú správu (výpočet je ďalej nekorektný).
Nasledovný program nájde maximálne číslo v súbore celých čísel:

var
  Subor: TextFile;
  Cislo, Max: Integer;
begin
  AssignFile(Subor, 'text.txt');
  Reset(Subor);
  Max := Low(Integer);
  while not Eof(Subor) do
  begin
    Read(Subor, Cislo);
    if Cislo > Max then
      Max := Cislo;
  end;
  CloseFile(Subor);
  if Max = Low(Integer) then
    Memo1.Lines.Append('súbor je prázdny')
  else
    Memo1.Lines.Append('maximálne číslo v súbore je ' + IntToSTr(Max));
end;

Poznámky

  • ak súbor obsahoval napr. len prázdny riadok (alebo len medzery), tak program vypíše, že maximum bolo 0
  • toto isté sa stane, ak súbor obsahuje len záporné čísla a za posledným číslom sú ešte nejaké medzerové znaky - funkcia Eof(Subor) za posledným číslom vráti False - ešte nie je koniec súboru, ale už tam nie je žiadne číslo, teda prečíta sa hodnota 0
Koniec riadka a súboru s filtrovaním medzier

Niekedy sa stáva situácia (napr. pri čítaní čísel), že štandardné funkcie Eof a Eoln oznámia, že ešte nie je koniec súboru, resp. riadka, pritom tam ostali len medzerové znaky, ktoré ale už nepotrebujeme interpretovať ako vstup. Tu výhodne využijeme upravené funkcie SeekEof a SeekEoln, ktoré pracujú rovnako ako ich pôvodné verzie, ale každá najprv odfiltruje medzerové znaky a až potom vráti informáciu o konci súboru, resp. riadka.

  • štandardná funkcia SeekEof(Subor) najprv odfiltruje medzerové znaky (medzera, <Eoln> alebo #9) a až potom otestuje koniec súboru,
  • štandardná funkcia SeekEoln(Subor)najprv odfiltruje medzerové znaky (medzera a #9) a až potom otestuje koniec riadka,

Predchádzajúci program by už teraz nemal mať predtým uvedené problémy:

var
  Subor: TextFile;
  Cislo, Max: Integer;
begin
  AssignFile(Subor, 'text.txt');
  Reset(Subor);
  Max := Low(Integer);
  while not SeekEof(Subor) do
  begin
    Read(Subor, Cislo);
    if Cislo > Max then
      Max := Cislo;
  end;
  CloseFile(Subor);
  if Max = Low(Integer) then
    Memo1.Lines.Append('súbor je prázdny')
  else
    Memo1.Lines.Append('maximálne číslo v súbore je ' + IntToSTr(Max));
end;

Zápis čísel

Už vieme, že pomocou Write(Subor, ...) môžeme do textového súboru zapisovať aj čísla, t.j. hodnoty číselných výrazov (Integer alebo Real):

  • celé čísla sa zapíšu bez medzery pred číslom aj za číslom, t.j. Write(Subor, I, I+1) pre i=17 zapíše 1718
  • reálne čísla sa do súboru zapisujú v semilogaritmickom tvare s medzerou pred číslom

Príklad: Všetky čísla zo súboru "text1.txt" budeme kopírovať do súboru "text2.txt" pričom ich budeme zaraďovať po troch do riadka:

var
  Subor1, Subor2: TextFile;
  Cislo, Pocet: Integer;
begin
  AssignFile(Subor1, 'text1.txt');
  Reset(Subor1);
  AssignFile(Subor2, 'text2.txt');
  Rewrite(Subor2);
  Pocet := 0;
  while not SeekEof(Subor1) do
  begin
    Read(Subor1, Cislo);
    if Pocet = 3 then
    begin
      Writeln(Subor2);
      Pocet := 1;
    end
    else
    begin
      if Pocet>0 then
        Write(Subor2, ' ');
      Inc(Pocet);
    end;
    Write(Subor2, Cislo);
  end;
  CloseFile(Subor1);
  CloseFile(Subor2);
  Memo1.Lines.LoadFromFile('text2.txt');
end;

V súbore sú reálne čísla, máme zistiť počet čísel, ktoré majú hodnotu menšiu ako je priemer všetkých čísel v súbore - tieto "podpriemerné" čísla zapíšeme do druhého súboru.

var
  Subor, Vystup: TextFile;
  Cislo, Suma, Priemer: Real;
  Pocet: Integer;
begin
  AssignFile(Subor, 'text1.txt');
  Reset(Subor);           //     priradenie vstupného súboru a jeho
                    otvorenie na čítanie
  Suma := 0;
  Pocet := 0;   // v premennej Pocet zistíme počet všetkých čísel
  while not SeekEof(Subor) do
  begin
    Read(Subor, Cislo);
    Suma := Suma+Cislo;        // Inc(Suma, Cislo);
    Inc(Pocet);
  end;
  Reset(Subor);                   // !!! nastavenie pozície na
                                            začiatok súboru !!!
  AssignFile(Vystup, 'text2.txt');
  Rewrite(Vystup);
  Priemer := Suma / Pocet;
  Pocet := 0;            // premennú Pocet využijeme na
                             zistenie počtu podpriemerných čísel
  while not SeekEof(Subor) do
  begin
    Read(Subor, Cislo);
    if Cislo < Priemer then
    begin
      Inc(Pocet);
      Writeln(Vystup, Cislo:0:2);
    end;
  end;
  CloseFile(Subor);
  CloseFile(Vystup);
  Memo1.Lines.Append('počet podpriemerných=' + IntToSTr(Pocet));
end;

Poznámka

  • všimnite si, že program vyhlási chybu na delení nulou, ak súbor neobsahoval ani jedno číslo
Formátovací parameter vo Write

Formátovací parameter môžeme uviesť za ľubovoľnú vypisovanú hodnotu. Zapisuje sa zo znakom ":" a označuje šírku poľa, do ktorej sa daná hodnota vypíše. Ak je vypisovaná hodnota kratšia ako šírka poľa, tak sa zľava doplní medzerami, inak sa tento parameter ignoruje. Napr.

  • za znakom alebo znakovým reťazcom
    Write(Subor, '*':10);
    označuje, že znak sa vypíše na šírku 10, t.j. najprv 9 medzier a potom '*'
    Write(Subor, 'delphi':3);
    nakoľko reťazec je dlhší ako formátovací parameter, formát sa ignoruje a vypíše sa kompletný reťazec
  • formátovací parameter za celým číslom označuje šírku, do ktorej sa má zapísať číslo, ak by nevošlo do danej šírky, formát sa ignoruje
    Write(Subor, 25*25:5);
    zapíše dve medzery, za ktoré dá číslo 625
  • formátovací parameter za reálnym číslom tiež označuje šírku, číslo sa vypíše v semilogaritmickom tvare; druhý formátovací parameter označuje počet desatinných miest
    Write(Subor, Sin(2):15);
    zapíše 9.092974E-0001
    Write(Subor, Cos(2):7:4);
    zapíše -0.4161
  • • logické hodnoty True a False sa vypisujú rovnako ako znakové reťazce 'TRUE' a 'FALSE', preto tiež môžeme uviesť formátovací parameter, pre ktorý zrejme nemá zmysel, aby bol menší ako 5
    Write(Subor, 3*7>22:6, (L1 = L2) or (L1 <> L2):6)
    zapíše FALSE TRUE
Späť

Vyhľadávanie

(c) 2008 Všetky práva vyhradené.