Práca so súbormi. Konzolová aplikácia.
14.02.2010 21:44Prá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)
7 BEL bell zvonček
8 BS backspace späť
9 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 a 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äť