Textové súbory

S pojmom súbor sme sa už v pascale stretli - napísané programy odkladáme do textového súboru pod názvom *.pas a preložený program (binárny súbor) sa vytvorí pod názvom *.exe. Teraz sa naučíme pracovať so súbormi v programe - vytvárať ich, editovať, čítať. Doteraz sme každý vstup dát do programu museli realizovať cez klávesnicu a výstup sme smerovali len na obrazovku. Vieme, že na disku sú adresáre a v nich súbory. Niektoré súbory možme prezerať, meniť bežnými editormi (napr. editorom v Norton Commanderi alebo pascalským editorom v ktorom píšete programy) a takéto súbory nazývame textové súbory.
Ostatné súbory nazývame binárne - pascal má nástroje aj pre prácu s takýmito súbormi a tu sa im nebudeme venovať.
Deklarácia
Prvé čo potrebujeme urobiť je vytvoriť premennú, ktorá zabezpečí spojenie s nejakým súborom na disku.
Syntax:
<premenná> : text
Napr.:
Var
f : text;
Prepojenie na súbor
Do premennej typu text potrebujeme zapísať názov súboru (poprípade názov aj s cestou). Na tento účel máme príkaz assign. Bez tohoto príkazu nie je možné používať ďalšie príkazy pre prácu so súbormi.
Syntax príkazu assign:
assign(<premenná> ,<stringový výraz>)
premenná - musí byť typu string.
stringový výraz - po vyhodnotení v ňom musí byť názov súboru. Názov môže byť zadaný aj s cestou.

Sémantika:
Priradí meno zo stringového výrazu do uvedenej premennej. Vytvorí spojenie medzi súborom a premennou. Súbor s daným menom nemusí existovať.
Príklady:
assign (f,'data.txt') - spojí f so súborom data.txt v aktuálnom adresári.
Aktuálny adresár možno nastaviť z menu "File" voľbou "Change dir"
assign (f,'data\zoznam.dat') - spojí f so súborom "zoznam.dat" v podadresári "data" aktuálneho adresára
assign (f,'\aa.mem') - spojí f so súborom "aa.mem" v hlavnom adresári aktuálneho disku.
Zrušenie súbora
Súbor, ktorý nie je otvorený príkazom reset alebo rewrite je možné zrušiť (samozrejme až po assign).
Syntax príkazu erase:
erase(<premenná>)
premenná - musí byť typu text. Či sa akcia podarila je možné testovať funkciou ioresult (bude prebrané ďalej).
Sémantika:
ak existuje súbor prepojený s premennou, tak súbor bude zrušený inak nastane chyba. Chybu možno ošetriť pomocou funkcie ioresult.
Premenovanie súbora
Súbor, ktorý nie je otvorený príkazom reset alebo rewrite je možné premenovať (samozrejme až po assign).
Syntax príkazu rename:
rename(<premenná>, <stringový výraz>)
premenná - musí byť typu text
stringový výraz - tu nusí byť nové prípustné meno súboru.
Sémantika príkazu rename:
Súbor, ktorý je spojený s uvedenou premennou zmení meno. Ak je meno neregulérne príkaz zahlási chybu - je možné ošetriť pomocou ioresult. Súbor nesmie byť otvorený na čítanie, resp. zápis.
Otvorenie súboru
Ak chceme súbor čítať, vytvárať alebo pripisovať údaje do súboru musíme súbor otvoriť. Poznáme 3 príkazy na otvorenie súbora:
  1. reset( premenná ) - otvorí súbor na čítanie
  2. rewrite ( premenná ) - otvorí súbor na zápis
  3. append( premenná ) - otvorí súbor na pripisovanie
Aby sme s takýmito súbormi mohli pracovať potrebujeme rozumieť jeho štruktúre. Textový súbor sa skladá z viet, vety sú ľubovoľne dlhé ale ukončené musia byť dvojicou znakov CR a LF (CR=#13, LF=#10). Takýto súbor si môžeme vytvoriť, čítať pomocou pascalského programu, ale môžeme si ho naklepať resp. prezerať aj v nejakom inom systémovom textovom "editore" napr. pascalským editorom, norton editorom. Pri klepaní tex. súboru vloženie znakov CR LF zabezpečíme stlačením klávesy ENTER. Tieto znaky sú na monitore "neviditelné" zabezpečia zalomenie riadku.
Čítanie súboru
Reset (premenná) - otvorí súbor na čítanie a nastaví sa na prvú vetu. Ak súbor neexistuje príkaz zahlási chybu. Vety je možné čítať len v tom poradí v akom sú v súbore zapísané. Na čítanie údajov zo súboru používame príkazy
read
readln
Syntax príkazov read, readln
read (premenná typu text, zoznam premenných oddelených čiarkou)
readln (premenná typu text, zoznam premenných oddelených čiarkou)
Na zoznam premenných platia tie isté pravidlá ako pri čítaní z klávesnice.
Sémantika
Čítanie zo súboru prebieha úplne rovnako ako sme poznali pri čítaní z klávesnice. Číselné údaje musia byť oddelené medzerami, tabelátormi alebo znakmi CRLF, čo je rovnaké ako pri čítaní z klávesnice. Príkaz redln() po naplnení všetkých premenných prejde v súbore "kurzorom" na nasledujúci riadok, read() nechá kurzor tam kde skončil.
Teraz si ukážeme na príklade ako môžeme súbor načítať a vypísať na obrazovke. Súbor si vytvoríme v pascalskom okne a dáme mu meno "data.dat". Súbor je tvorený 4 riadkami a v každom sú dve 2 celé čísla. Zatiaľ sa obmedzíme na prechod súboru pomocou príkazu for (musíme poznať minimálne počet riadkov, alebo počet čísel v súbore)
8 15
-120 15
200 105
8 -15
_

Program prog1;
{Nacitanie suboru pomocou premennej typu string}
uses crt;
Var f: text;
    i: byte;
    s: string;
Begin
assign(f,'data.dat');
reset(f);
for i:=1 to 4 do
  Begin
    readln(f,s);
    writeln(s);
  End;
End.
Program prog2;
{Nacitanie suboru pomocou premennej typu char}
{pocet znakov v subore 4+2+7+2+7+2+5+2=31}
uses crt;
Var f: text;
    i: byte;
    c: char;
Begin
assign(f,'data.dat');
reset(f);
for i:=1 to 31 do
  Begin read(f,c); write(c); End;
End.
Program prog3;
{Nacitanie suboru pomocou premennej typu integer}
uses crt;
Var f: text;
    i: byte;
    c: integer;
Begin
assign(f,'data.dat');
reset(f);
for i:=1 to 4 do
  Begin
    read(f,c); write(c);
    read(f,c); writeln(c);
  End;
End.

Všetky tieto programy dajú rovnaké výsledky, no ak potrebujeme s číslami v súbore urobiť nejaké operácie tak samozrejme použijeme postup v prog3.
Premyslite, ako sa v prog2 výpis súboru urobil do riadkov, keď sme nepoužili writeln.
Príklady na precvičenie:
  1. Upravte prog3 tak, aby navyše pod výpis súboru vypísal súčet všetkých čísel v súbore.
  2. Upravte prog3 tak, aby navyše v každom riadku bol navyše súčet všetkých čísel v riadku.
  3. Upravte program aby sa riadky vypísali zrkadlovo odzadu (napr. 8 15 -> 51 8).
  4. Vytvorte súbor s celými číslami typu integer, neznámy počet riadkov neznámy a premenlivý počet čísel v riadku.
    Vypíšte tento súbor.
    Spočítajte všetky čísla v súbore.
  5. Pre ľahšiu prácu so súborom, sa do prvého riadku súboru napíše koľko ďalších riadkov s dátami v súbore je.
    Vytvor súbor data.001 s s údajom o počte riadkov v prvom riadku N. V ďalších N riadkoch budú po tri čísla. Vypíš tento súbor a napíš v ktorom riadku je max. súčet.
  6. Ak počet čísel v riadku "kolíše" je možné ako prvé číslo v riadku uviesť počet dát v tomto riadku (ak je počet dát 0 nie sú v riadku dáta).
    Vytvor súbor data.002, kde v prvom riadku je počet dátových riadkov N a v každom dátovom riadku je v prvom čísle počet dát v riadku Mi.
    - Vypíš dáta zo súboru (bez čísel N a Mi)
    - Zisti v ktorom riadku je najväčšie číslo
    - Zisti riadok v ktorom je maximálny súčet dát Nájdi riadok s
Zápis do súboru
Súbor otvoríme na zápis pomocou príkazu rewrite(<premenná>). Ak súbor neexistuje, rewrite vytvorí prázdny súbor, ak súbor existuje je "vyprázdnený". Ak súbor nie je možné vytvoriť alebo skrátiť jeho dĺžku na nulu príkaz končí chybou, ošetrovanie chýb a ich kontrola pomocou ioresult viď ošetrenie chýb. Príkazy pre zápis do súboru:
write(<premenná typu text>, zoznam výrazov oddelených čiarkami)
writeln (<premenná typu text>, zoznam výrazov oddelených čiarkami) Pravidlá pre zoznam výrazov sú rovnaké aké sme poznali pri zapisovaní na obrazovku. Číselné a logické hognoty sú prevedené na text a zapísané do súboru. Pri príkaze writeln sa nakoniec ešte do súboru zapíšu znaky CRLF a veta je ukončená.
Príklad:
Program Tvorba_suboru;
var f:text;
    n,c:integer;
Begin
  write('Zadaj pocet viet: ');
  readln(n);
  assign(f,'cisla.txt');
  rewrite(f);
  writeln(f,n);
  for i:=1 to n do writeln(f,-50+random(101));
  close(f); {uzavrie subor}
end.

O tom, že súbor je vytvorený sa môžme ľahko presvedčiť tak, že si ho načítame do nového pascalského okna (F3 cisla.txt)
Príklady na precvičenie:
  1. Napíšte program, ktorý z čísel v súbore cisla.txt vytvorí súbory parne.txt a nepárne.txt, kde čísla párne padnú do sub parne.txt a nepárne do sub neparne.txt.
  2. Vytvorte program na zápis sponzorských darov škole. Z klávesnice zadáme počet sponzorov (N) a potom N-krát suma a meno sponzora.
    Súbor vytvorte tak, aby ste ho v pascalskom programe vedeli bez vstupu z klávesnice aj vypísať.
Pripisovanie do súboru
Súbor otvoríme na zápis pomocou príkazu append(<premenná>). Ak súbor neexistuje, append zahlási chybu, ak existuje nastaví sa na jeho koniec. Každý zápis pripisuje za poslednú vetu. Príkazy na zápis sú totožné s časťou "Zápis do súboru".
Zatvorenie súboru
Keď už nepotrebujeme zo súboru čítať resp. zapisovať doň je vhodné súbor zavrieť. Korektné ukončenie behu programu súbor uzavrie ale v prípade "pádu" nášho programu alebo oper. systému môže byť náš súbor poškodený.
Syntax
close(<premenná>)
Sémantika
Zavrie otvorený súbor, ak súbor nie je otvorený zahlási chybu.
Funkcie na testovanie konca súboru a riadku
Pri čítaní zo súboru bol problém ak sme nepoznali počet viet, alebo počet čísel v riadku. Na tento účel potrebujeme funkcie, ktoré nám dokážu povedať, či sme na konci riadku alebo celého súboru.
Eof()
Syntax
Eof(<premenná>):Boolean
Sémantika
Funkciu je možné používať na súbor otvorený na čítanie. Návratová hodnota funkcie je True, keď sme v súbore na konci (t.j. za posledným znakom súboru), ináč vráti False.
SeekEof()
Syntax
SeekEof(<premenná>):Boolean
Sémantika
Funkciu je možné používať na súbor otvorený na čítanie. Návratová hodnota funkcie je True, keď sme v súbore na "takmer" konci (t.j. ďalšie znaky sú len medzery, Tabelátory, Entre), ináč vráti False. Používa sa pri načítavaní čísel, ak za posledným číslom sú ešte napr. medzery Eof() by nevrátil True a ďalšie čítanie by už hlásilo chybu ale SeekEof() vráti True a my vieme, že viac čísel už v súbore nie je.
Eoln()
Syntax
Eoln(<premenná>):Boolean
Sémantika
Funkciu je možné používať na súbor otvorený na čítanie. Návratová hodnota funkcie je True, keď sme vo vete súboru na konci (t.j. pred znakmi CRLF), ináč vráti False.
Upozornenie: Napriek tomu, že posledná veta nemusí byť ukončená CRLF funkcia sa aj v poslednej vete chová korektne.
SeekEoln()
Syntax
SeekEof(<premenná>):Boolean
Sémantika
Funkciu je možné používať na súbor otvorený na čítanie. Návratová hodnota funkcie je True, keď sme v súbore na "takmer" konci vety(t.j. ďalšie znaky pred CRLF sú len medzery, Tabelátory, Entre), ináč vráti False. Používa sa pri načítavaní čísel, ak za posledným číslom a pred CRLF sú ešte napr. medzery Eoln() by nevrátil True a ďalšie čítanie by už prebehlo do ďalšieho riadku, ale SeekEoln() vráti True a my vieme, že viac čísel už v tejto vete súboru nie je.
Upozornenie: Napriek tomu, že posledná veta nemusí byť ukončená CRLF funkcia sa aj v poslednej vete chová korektne.
Príklady na precvičenie:
  1. Vytvor dátový súbor vstup.dat o 12-tich riadkoch v každom po 3 čísla. Vytvor výstupný súbor výstup.dat o 12-tich riadkoch, kde v i-tom riadku bude súčet čísel i-teho riadku súboru vstup.dat.
  2. Upravte program z predošlej úlohy tak, aby počet riadkov bol uvedený v prvom riadku súboru vstup.dat i výstup.dat
  3. Upravte program z predošlej úlohy tak, aby počet riadkov bol neznámy a bude rovnaký v súbore vstup.dat i výstup.dat
  4. Upravte program z predošlej úlohy tak, aby počet čísel v riadkoch bol neznámy a premenlivý.
  5. Napíš program, ktorý vypíše svoj pascalský program na obrazovku.
  6. Uprav predošlý program, aby názov vypisovaného súboru bol parametrom.
  7. Napíš program na kopírovanie súborov. Názvy súborov môžu vstupovať z klávesnice alebo byť ako parametre.
  8. Napíš program na evidenciu priateľov, evidovať chceme meno, adresu, tel. číslo. Program by mal poskytovať nasledovné menu:
    výpis, pridanie mena na koniec, zrušenie zadaného mena, koniec programu
Ošetrovanie chýb
Práca so súbormi môže viesť ku vzniku chyby. Najčastejšie dôvody vzniku chyby:
Súbor neexistuje pri otváraní resetom
Súbor je readonly, a teda nie je možné doň zapisovať
Disketa je plná
Disketa bola vybratá z mechaniky alebo je poškodená.

Program na každý vznik takejto chyby reaguje abnormálnym ukončením. Aby sme predišli tomuto padaniu programu môžeme dočasne alebo aj natrvalo vyradiť kontrolu I/O chýb.
Vypnutie kontroly {$I-}
Zapnutie kontroly {$I+}
Tým, že sme vypli kontrolu chýb musíme kontrolu, či nastala chyba robiť sami. Na tento účel máme funkciu ioresult: integer
Po skončení "riskantnej" operácie počas ktorej sme vypli kontrolu I/O chýb môžme pomocou ioresult zistiť, či sa operácia podarila.
Hodnota 0 znamená všetko v poriadku
Hodnota rôzna od 0 znamená číslo vzniklej chyby.
Pozn.: Opätovné volanie funkcie však vráti vždy nulu (po každej operácii máme nárok len na jeden test).