IX. |
TYPY SOUBOR |
Soubor je posloupnost dat, existující nebo vytvářená mimo operační paměť počítače na některém z připojených zařízení (disk, tiskárna, klávesnice…). V operačním systému DOS je soubor identifikován speciálním řetězcem znaků, zvaným systémová specifikace. Úplná systémová specifikace diskového souboru obsahuje označení zařízení (logického disku), cestu k adresáři a jméno souboru. Specifikace ostatních souborů obsahují pouze označení zařízení. Označení logického disku se skládá z písmene a dvojtečky (například a:). Toto jsou označení některých dalších běžných zařízení:
con konzola (klávesnice a obrazovka) prn tiskárna nul fiktivní zařízení
Fiktivní zařízení se používá především pro ladění programů. Výstup na toto zařízení se „ztrácí“, vstup z něj je prázdný.
Cesta obsahuje postupný výčet jmen adresářů na trase k souboru. Položky výčtu jsou vzájemně odděleny znakem obrácené lomítko (\), který se vkládá i mezi cestu a označení zařízení a mezi cestu a jméno souboru. Všechna jména jsou maximálně osmiznaková, mohou však být doplněna až tříznakovou příponou, která je pak od (hlavní části) jména oddělena tečkou.
Podrobnější informace o struktuře systémové specifikace souboru lze nalézt v dokumentaci operačního systému DOS.
Kromě dalších atributů souboru uchovává operační systém především informaci o jeho velikosti, čímž se rozumí počet bytů dat, kterými je soubor tvořen. Velikost je omezena kapacitou média, na němž se soubor nachází.
Z pohledu programovacího jazyka Turbo Pascal je soubor datovým objektem — abstrakcí bez konkrétních fyzických vlastností souborů operačního systému. Jazyk definuje soubor jako posloupnost komponent stejného typu, předem neznámé a neomezené délky. Umístění komponenty v souboru charakterizuje její pozice (index), udávající pořadí komponenty od začátku souboru. Číslování začíná od nuly (počáteční komponenta se nachází na pozici nula).
Základními operacemi nad souborem jsou čtení aktuální komponenty a zápis (do) aktuální komponenty — komponenty na aktuální pozici v souboru. Hodnota aktuální pozice je udržována automaticky — po zpracování komponenty operací čtení nebo zápisu je automaticky inkrementována, takže „ukazuje“ na komponentu následující.
IX.1. |
Proměnné typu soubor |
soubor textový binární
Textové soubory se používají pro komunikaci programu s uživatelem. Data jsou v nich uložena ve vnější reprezentaci (jako při výpisu na displej), takže jsou uživatelem přímo čitelná, zato při jejich načítání resp. zápisu programem musí být prováděny vstupně-výstupní konverze z vnější na vnitřní reprezentaci resp. z vnitřní na vnější reprezentaci. Jako textové jsou zpracovány pouze soubory, jejichž obslužné proměnné jsou standardního (jazykem předdeklarovaného) typu Text (nebo jeho identického ekvivalentu).
Binární soubory jsou určeny pro komunikaci mezi programy či ukládání dat programem před jejich pozdějším zpracováním. Obsahují data v příslušném vnitřním tvaru (žádné vstupně-výstupní konverze se neprovádějí), takže nejsou uživatelem přímo čitelné. Podle způsobu zpracování jsou klasifikovány na soubory typové a netypové.
binární soubor typový netypový
Jako typové jsou zpracovány ty soubory, jejichž obslužná proměnná má v popisu svého typu (obrázek 44) uveden typ komponent souboru, uvozený rezervovanými slovy file of. Takové soubory jsou pak chápány jako posloupnosti hodnot specifikovaného typu. Pokud specifikace typu komponent chybí, tj. je-li v popisu typu soubor uvedeno pouze file, je proměnná zástupcem souboru netypového. Komponenty netypových souborů jsou chápány jako bloky bytů, jejichž vnitřní struktura není při zpracování souboru důležitá.
Obrázek 44: Popis typu soubor
Popis typu soubor může být (stejně jako popis jiného typu) uveden buď přímo v deklaraci proměnné nebo v deklaraci identifikátoru typu. Typ komponent nesmí být soubor ani strukturovaný typ s komponentou typu soubor na jakékoliv úrovni struktury, jinak smí být libovolný.
Těmito deklaracemi jsou zavedeny čtyři proměnné typu soubor. Každý fyzický soubor, který bude později spojen s proměnnou F1, bude chápán jako posloupnost reálných čísel, soubor spojený s proměnnou F2 jako posloupnost komponent typu Kniha, soubor spojený s proměnnou F3 jako posloupnost polí uvedeného typu a soubor spojený s proměnnou F4 jako posloupnost bloků bytů, bez definované vnitřní struktury bloků.type RealFile = file of Real; Kniha = record Autor, Nazev : string [30]; Editor : string [15]; RokVydani : Word end; var F1 : RealFile; F2 : file of Kniha; F3 : file of array [1..10] of Integer; F4 : file;
Deklarace proměnné F3 je sice z hlediska syntaxe bezchybná, avšak pro zpracování komponent fyzického souboru nepoužitelná. Jak procedura čtení komponenty ze souboru, tak procedura zápisu komponenty do souboru totiž vyžadují identitu typu komponent souboru a typu proměnné, do níž je komponenta načítána resp. jejíž hodnota má být jako komponenta do souboru zapsána. Pro splnění podmínky identity je nutné, aby typ komponent byl pojmenovaný (aby byl v popisu typu soubor uveden identifikátor typu komponent, jako například v deklaraci proměnné F2).
IX.2. |
Zpracování souborů v programu |
Jak bylo již v předchozím textu řečeno, deklarace proměnné typu soubor v sobě nese informaci o tom, zda soubor s ní spojený je textový, typový nebo netypový. Obecné standardní podprogramy, používané pro zpracování souborů, modifikují svou činnost podle toho, do které třídy aktuální soubor patří. Kromě obecných pak existují i podprogramy speciální, které lze použít pouze pro soubory určité třídy.
Zpracování souboru programem vyžaduje tuto posloupnost akcí (realizovaných voláním zmíněných podprogramů), jejichž pořadí nelze zaměnit:
Po zavření souboru jej lze znovu otevřít a pokračovat v jeho zpracování nebo opakovat uvedenou posloupnost akcí pro tutéž proměnnou a jiný (další) fyzický soubor.
Spojení proměnné se souborem Spojením proměnné s fyzickým souborem je míněna akce, kterou je specifikován konkrétní fyzický soubor, jejž bude dále uvažovaná proměnná v programu zastupovat.
procedure Assign (var F: soubor; Spec: string);
Spojí proměnnou F (libovolného typu soubor) se souborem, jehož systémová specifikace je hodnotou parametru Spec. Pokud je hodnotou parametru Spec prázdný řetězec, je proměnná spojena se standardním systémovým zařízením con.
Program, jehož fragment je uveden výše, pracuje se soubory Vstup a Vystup. Specifikace souboru Vstup se zadává až za běhu programu z klávesnice, kdežto specifikace souboru Vystup je pevně daná.var Vstup, Vystup : Text; Specifikace : string; ... Write('Soubor vstupních dat: '); Readln(Specifikace); Assign(Vstup, Jmeno); Assign(Vystup, 'abcd.txt'); ...
Otevření souboru Otevření souboru zahrnuje různé akce, spojené s inicializací přístupu k souboru — vyhledání fyzického souboru na disku, případně jeho založení, přidělení komunikačního kanálu, počáteční nastavení aktuální pozice v souboru a pod. Realizuje se voláním některé z procedur Reset, Rewrite nebo Append. Procedury Reset a Rewrite jsou univerzální, modifikují svou činnost podle typu otvíraného souboru, kdežto procedura Append je určena výhradně pro otevírání souborů textových.
Volba vhodné procedury závisí na tom, zda otevíraný soubor již existuje nebo má být vytvořen, a na tom, zda má být použit pro čtení nebo zápis dat.
Zpracování komponent souboru Pro každou třídu souborů existují podprogramy pro zpracování jejich komponent. Jedná se především o procedury pro zápis a čtení komponent a různé funkce, indikující stav souboru.
Zavření souboru Při zavírání jsou ve fyzickém souboru realizovány dosud nevykonané změny (vyprázdnění vyrovnávací paměti, aktualizace údajů v hlavičce souboru) a je uvolněn použitý komunikační kanál. Opomenutí zavření vytvořeného souboru vede zpravidla ke ztrátě dat.
procedure Close (var F: soubor);
Zavře soubor, spojený s parametrem F (proměnnou libovolného typu soubor).
IX.3. |
Textové soubory |
Výskytem dvojic znaků CR (#13) a LF (#10) je obsah textového souboru členěn na řádky, kdežto soubory ostatních typů tuto vnitřní organizaci komponent postrádají. Pouze u textových souborů lze proto například testovat stav „konec řádky“.
Za svou „přívětivost k uživateli“ textové soubory platí dvěmi omezujícími vlastnostmi: lze je zpracovávat pouze sekvenčně (postupně připisovat na konec souboru resp. postupně číst od začátku souboru) a nelze je otevřít pro současné čtení i zápis.
Otevření textového souboru Textový soubor je možno otevřít některým ze tří způsobů — pro čtení, pro zápis nebo pro přípis. Způsob otevření je dán volbou jedné z procedur Reset, Rewrite nebo Append. Proměnná, která je uvedena jako parametr procedury, již musí být spojena s konkrétním fyzickým souborem, jinak dojde k chybě. Otevírání již otevřeného souboru naopak chybou není, před znovuotevřením je totiž soubor zavřen automaticky.
procedure Reset (var F: Text);
Otevře pro čtení textový soubor spojený s proměnnou F. Fyzický soubor musí v okamžiku otvírání již existovat, jinak dojde k chybě. Ukazatel aktuální pozice je nastaven na začátek souboru, ukazuje tedy na první znak. Ze souboru lze pak pouze číst, nelze do něj zapisovat.
procedure Rewrite (var F: Text);
Textový soubor, spojený s proměnnou F, založí (vytvoří nový) a otevře pro zápis. Pokud specifikovaný soubor již existuje, je nejprve smazán (!), nelze takto otevřít existující soubor. Po otevření není v souboru žádný znak, ukazatel aktuální pozice je tedy nastaven současně na začátek i konec souboru. Do souboru lze pak pouze zapisovat, nelze z něj číst.
procedure Append (var F: Text);
Otevře pro přípis textový soubor spojený s proměnnou F. Fyzický soubor musí v okamžiku otvírání již existovat (jinak dojde k chybě), nelze takto založit nový soubor. Ukazatel aktuální pozice je nastaven na konec souboru (za poslední platný znak). Dále se soubor chová jako po otevření procedurou Rewrite — lze do něj pouze zapisovat, nelze z něj číst.
Proměnné Input a Output Jazykem jsou předdeklarovány dvě proměnné typu Text — proměnná Input a proměnná Output.
var Input, Output: Text;
Obě proměnné jsou součástí každého programu. Na začátku jeho běhu jsou automaticky spojeny se standardním vstupním resp. výstupním zařízením operačního systému, ty jsou pak automaticky otevřeny a při ukončení běhu programu automaticky uzavřeny:
Jinak jsou Input a Output obyčejnými proměnnými typu Text, které lze volně používat stejným způsobem, jako proměnné deklarované uživatelsky.Assign (Input, ''); Reset (Input); Assign (Output, ''); Rewrite (Output); ... { uživatelská část programu } Close (Input); Close (Output)
Zpracování textového souboru Do textového souboru lze vždy buď pouze sekvenčně zapisovat nebo z něj pouze sekvenčně číst. Sekvenční přístup spočívá v automatickém přesunu ukazatele aktuální pozice na následníka právě přečteného resp. zapsaného znaku v souboru. Ze souboru, který je otevřen pro čtení, lze tedy znaky číst pouze postupně, k již přečteným znakům se nelze vracet (leda znovuotevřením souboru), ani nelze znaky přeskakovat. Do souboru otevřeného pro zápis lze podobně zapisovat nové znaky pouze na jeho konec, nelze přepisovat znaky již zapsané.
Pro zpracování obsahu textového souboru jsou k dispozici procedury zápisu Write a Writeln, procedury čtení Read a Readln a testovací funkce EOF, EOLn, SeekEOF a SeekEOLn.
Činnost všech těchto podprogramů a předepsaný tvar jejich volání již byly zevrubně popsány v kapitole III. Nebylo však řečeno, že na prvním místě v seznamu jejich parametrů může být uvedena proměnná typu Text. Pokud není uvedena, aplikuje se činnost podprogramu na soubor proměnné Input (Read, Readln, EOF, SeekEOF, EOLn, SeekEOLn) resp. Output (Write, Writeln). Pokud však uvedena je, aplikuje se činnost podprogramu na soubor spojený s ní. Implicitně zpracovávaným souborem je tedy soubor proměnné Input pro vstupní operace resp. soubor proměnné Output pro výstupní operace.
9 Plášek Zdeněk 1 2 3 2 2 3 4 1 2 Marek Miloslav 1 1 2 1 1 2 3 1 1 Holubářová Lenka 2 2 3 3 2 3 4 1 2 Holub Ladislav 1 2 2 1 1 3 3 1 1 Koutský Dušan 1 1 2 1 1 2 2 1 1 Vaňousová Iva 1 1 2 2 2 3 3 1 1
Číslo v první řádce má význam počtu klasifikovaných předmětů. V dalších řádkách následují jména jednotlivých studentů a jejich závěrečné známky (v nějakém pevně daném pořadí předmětů). Počet studentů není předem znám, avšak není příliš velký. Klasifikační tabulku vytiskne program do výstupního textového souboru podle tohoto vzoru:
Koutský Dušan 1 1 2 1 1 2 2 1 1 1.33 Marek Miloslav 1 1 2 1 1 2 3 1 1 1.44 Holub Ladislav 1 2 2 1 1 3 3 1 1 1.67 Vaňousová Iva 1 1 2 2 2 3 3 1 1 1.78 Plášek Zdeněk 1 2 3 2 2 3 4 1 2 2.22 Holubářová Lenka 2 2 3 3 2 3 4 1 2 2.44 1.17 1.50 2.33 1.67 1.50 2.67 3.17 1.00 1.33 1.81
Jednotlivým studentům odpovídají jednotlivé řádky tabulky, poslední údaj na řádce je studijní průměr studenta. Řádky jsou podle tohoto údaje v tabulce seřazeny. Poslední řádka tabulky obsahuje klasifikační průměry třídy v jednotlivých předmětech a na posledním místě celkový průměr známek všech studentů ze všech předmětů.
{======================================================================} program Klasifikace; {======================================================================} const MaxZnamek = 10; { maximální počet známek } MaxStudentu = 30; { maximální počet studentů } type Student = record { struktura studenta } Jmeno : string [20]; Znamky : array [1..MaxZnamek] of 1..5; Prumer : Real end; Trida = array [1..MaxStudentu] { struktura třídy } of Student; var Soubor : Text; { vstupní a později } { výstupní textový soubor } Spec : string; { systémová specifikace souboru } T : Trida; { tabulka údajů } Studentu, { skutečný počet studentů } Znamek, { skutečný počet známek } S, { index studenta } Z : Byte; { index známky } Soucet : Word; { součet známek studenta a } { později součet všech známek } Sum : array [1..MaxZnamek] { součty známek všech stud. } of Byte; { v jednotlivých předmětech } Pom : Student; { pomocné proměnné } I, J : Byte; begin { program } Writeln; { tisk titulku } Writeln('KLASIFIKACE'); Writeln; Write('vstupní soubor : '); { dotaz na specifikaci souboru, } Readln(Spec); { obsahujícího vstupní data } Assign(Soubor, Spec); { inicializace souboru pro čtení } Reset(Soubor); S := 0; { inicializace indexu studenta } Readln(Soubor, Znamek); { načtení skutečného počtu známek} for Z := 1 to Znamek do Sum[Z] := 0; { inicializace součtů známek } while not SeekEOF(Soubor) do { dokud není vstupní soubor celý } begin { zpracován } Inc(S); { index dalšího studenta } Soucet := 0; { inicializace součtu jeho známek} with T[S] do begin Readln(Soubor, Jmeno); { načtení jména } for Z := 1 to Znamek do { pro všechny známky } begin Read(Soubor, Znamky[Z]); { načtení známky } Inc(Soucet, Znamky[Z]); { přičtení do součtu studenta } Inc(Sum[Z], Znamky[Z]) { přičtení do součtu předmětu } end; { další známka } Prumer := Soucet/Znamek { studijní průměr studenta } end; Readln(Soubor) { nová řádka } end; { další student } Studentu := S; { skutečný počet studentů } Close(Soubor); { zavření zpracovaného souboru } for S := 1 to Studentu-1 do { seřazení tabulky studentů } begin { podle studijních průměrů } I := S; for J := S+1 to Studentu do if T[I].Prumer > T[J].Prumer then I := J; if I <> S then begin Pom := T[S]; T[S] := T[I]; T[I] := Pom end end; Write('výstupní soubor : '); { dotaz na specifikaci souboru } Readln(Spec); { výstupních dat } Assign(Soubor, Spec); { jeho inicializace pro zápis } Rewrite(Soubor); for S := 1 to Studentu do { pro všechny studenty } with T[S] do begin Write(Soubor, Jmeno, ' ':21-Length(Jmeno)); { výpis jména studenta } for Z := 1 to Znamek do { pro všechny jeho známky } Write(Soubor, Znamky[Z]:5); { výpis známky } Writeln(Soubor, Prumer:8:2) { výpis studijního průměru } end; Writeln(Soubor); { prázdná řádka } Write(Soubor, ' ' : 24); { mezery do začátku sloupce } { známek prvního předmětu } Soucet := 0; { inicializace součtu všech } { známek ze všech předmětů } for Z := 1 to Znamek do { pro všechny předměty } begin Write(Soubor, Sum[Z]/Studentu:5:2);{ tisk průměru } Inc(Soucet, Sum[Z]) { aktualizace součtu } end; Writeln(Soubor, Soucet/Studentu/Znamek:5:2); { celkový průměr } Close(Soubor) { zavření výstupního souboru } end. { program }
IX.4. |
Typové soubory |
Pro ilustraci poslouží jednoduchý příklad: Chceme vytvořit soubor, obsahující jediný údaj — číslo 65. Vytvoříme-li jej jako soubor typu file of Byte, bude obsahovat jediný byte s hodnotou 65. Pokud bychom ovšem později četli tento soubor jako textový, byl by jeho jediný byte interpretován jako znak #65 neboli 'A'. Bude-li soubor naopak vytvořen jako textový, bude obsahovat dva byty — znak '6' a znak '5'. Při čtení tohoto souboru jako file of Byte by pak první z jeho bytů byl interpretován jako číslo 54 ('6' = #54) a druhý jako číslo 53 ('5' = #53).
Na rozdíl od textových lze binární (typové i netypové) soubory otevřít pro současné čtení i zápis a je umožněno jejich index-sekvenční zpracování (výběr aktuální komponenty) — po přečtení či zápisu komponenty je sice ukazatel aktuální pozice v souboru automaticky inkrementován, v kterémkoliv okamžiku po otevření souboru jej však lze nastavit i explicitně.
Otevření typového souboru Pro otevření existujícího souboru je třeba použít proceduru Reset, pro založení nového souboru proceduru Rewrite. Obě pracují obdobně jako při otvírání souboru textového.
procedure Reset (var F: typový soubor);
procedure Rewrite (var F: typový soubor);
Obě procedury otevírají fyzický soubor spojený s parametrem F a ukazatel aktuální pozice v souboru nastavují na jeho začátek. Fyzický soubor, otevíraný procedurou Reset, musí v okamžiku jejího volání již existovat, kdežto Rewrite případně existující fyzický soubor smaže a založí nový. Procedura Append nemůže být pro otevření typového souboru použita — otevření existujícího typového souboru pro přípis lze dosáhnout procedurou Reset a následným nastavením ukazatele aktuální pozice na konec souboru (procedura Seek).
Při použití procedury Rewrite je soubor vždy otevřen současně pro čtení i pro zápis (jeho komponenty lze číst i zapisovat), kdežto při použití procedury Reset závisí režim přístupu k souboru na hodnotě standardní proměnné FileMode.
Definuje režim přístupu k binárnímu souboru, otvíranému procedurou Reset. Všeobecně platné hodnoty proměnné a jim příslušející režimy přístupu k souboru jsou:
kód | režim | |||||||
---|---|---|---|---|---|---|---|---|
0 | pouze pro čtení | |||||||
1 | pouze pro zápis | |||||||
2 | pro čtení i zápis |
Přiřazení nové hodnoty proměnné FileMode způsobí, že všechna další volání procedury Reset vezmou tuto hodnotu v úvahu. Implicitní hodnotou (při startu programu) je 2.
Zpracování typového souboru Pro čtení aktuální komponenty a zápis do aktuální komponenty jsou stejně jako u textových souborů k dispozici procedury Write a Read. Neprovádějí však žádné vstupně-výstupní konverze a jejich modifikace Writeln a Readln nejsou povoleny (obsah binárních souborů není členěn na řádky).
procedure Read (var F: typový soubor; var X1[, X2...]: typ komponent);
Přečte do proměnných X1, X2... příslušný počet komponent ze souboru, spojeného s proměnnou F. Všechny parametry X1, X2... musí být proměnné toho typu, který je uveden jako typ komponent souboru v deklaraci typu proměnné F. Soubor musí být otevřen pro čtení (nebo pro čtení i zápis) a požadovaný počet komponent musí být k dispozici — čtení za koncem souboru je chybou.
procedure Write (var F: typový soubor; var X1[, X2...]: typ komponent);
Zapíše hodnoty proměnných X1, X2... do souboru F. Všechny parametry X1, X2... musí být opět striktně proměnné toho typu, který je uveden jako typ komponent souboru v deklaraci typu proměnné F (nemohou to být výrazy ani proměnné typů pouze kompatibilních s typem komponent souboru). Soubor musí být otevřen pro zápis (nebo pro čtení i zápis). Zapisovat lze na konec i dovnitř souboru (ve druhém případě jsou zápisem nových hodnot přepisovány existující komponenty souboru).
Zatímco zpracování komponent textových souborů je pouze sekvenční, typové (a netypové) soubory jsou zpracovávány index-sekvenčně. Procedury Read a Write sice čtou a zapisují komponenty sekvenčně od aktuální pozice v souboru, tu však lze kdykoliv od okamžiku otevření binárního souboru až do jeho zavření nastavit explicitně. Pro podporu index-sekvenčního zpracování komponent binárních souborů jsou implementovány následující podprogramy. Všechny předpokládají, že soubor F je již otevřen (jinak jejich volání způsobí chybu).var F: file of Char; C: 'a'..'z'; Z: Char; ... Write (F, Z, C); { chyba - C není typu Char }
function FileSize (var F: binární soubor): LongInt;
Vrací velikost souboru v komponentách (aktuální celkový počet komponent v souboru).
function FilePos (var F: binární soubor): LongInt;
Vrací hodnotu ukazatele aktuální pozice v souboru — index aktuální komponenty (počáteční komponenta má index 0).
procedure Seek (var F: binární soubor; Pozice: LongInt);
Nastavuje ukazatel aktuální pozice v souboru na hodnotu parametru Pozice.
procedure Truncate (var F: binární soubor);
Odstraní (vymaže) ze souboru aktuální komponentu a všechny následující.
function EOF (var F: binární soubor): Boolean;
K dispozici je rovněž funkce EOF, testující stav konec souboru. Vrací True v případě, kdy se ukazatel aktuální pozice v souboru nachází za jeho poslední komponentou (má hodnotu velikosti souboru v komponentách).
{======================================================================} program Knihovnik; {======================================================================} type Titul = record { struktura titulu } Autor : string [20]; Nazev : string [20]; Editor : string [10]; Rok : Word; Medium : (Kniha, MGF, LP, CD) end; var Knihovna : file of Titul; { datový soubor } JmenoKnihovny : string; { systémová specifikace } Akce : Char; { symbol zvolené operace } procedure ZalozeniKnihovny; {-------------------------} begin { ZalozeniKnihovny } Close(Knihovna); { zavření aktuální knihovny } Write('založit knihovnu : '); { zadání systémové specifikace } Readln(JmenoKnihovny); { zakládané knihovny } Assign(Knihovna, JmenoKnihovny); { a její inicializace } Rewrite(Knihovna) end; { ZalozeniKnihovny } procedure OtevreniKnihovny; {-------------------------} begin { OtevreniKnihovny } Close(Knihovna); { zavření aktuální knihovny } Write('otevřít knihovnu : '); { zadání systémové specifikace } Readln(JmenoKnihovny); { otvírané (existující) knihovny } Assign(Knihovna, JmenoKnihovny); { a její inicializace } Reset(Knihovna) end; { OtevreniKnihovny } procedure PripisTitulu; {---------------------} var T : Titul; { připisovaný titul } CisloMedia : Char; { pomocné označení média } begin { PripisTitulu } with T do { zadání nového titulu } begin Write('autor : '); { položka Autor } Readln(Autor); Write('název : '); { Nazev } Readln(Nazev); Write('editor : '); { Editor } Readln(Editor); Write('rok : '); { Rok } Readln(Rok); repeat { pomocné označení média } Write('médium (0-KNIHA 1-MGF 2-LP 3-CD) : '); Readln(CisloMedia) until (CisloMedia >= '0') and (CisloMedia <= '3'); case CisloMedia of { určení média } '0': Medium := Kniha; { z pomocného označení } '1': Medium := MGF; '2': Medium := LP; '3': Medium := CD end end; Seek(Knihovna, FileSize(Knihovna)); { nastavení pozice zápisu} { na konec knihovny } Write(Knihovna, T) { zápis titulu } end; { PripisTitulu } procedure VymazTitulu; {--------------------} var T : Titul; { mazaný titul } CisloTitulu : LongInt; { jeho číslo (pořadí v seznamu) } Volba : Char; { potvrzení požadavku smazání } I : LongInt; { pomocný index pozice v souboru } begin { VymazTitulu } Write('číslo titulu : '); { zadání čísla titulu } Readln(CisloTitulu); Writeln; if (CisloTitulu < 1) or (CisloTitulu > FileSize(Knihovna)) then Writeln('chybné číslo titulu') { zadané číslo mimo rozsah } else { přípustné číslo } begin Seek(Knihovna, CisloTitulu-1); { nastavení pozice v knihovně } Read(Knihovna, T); { načtení titulu uvedeného čísla } with T do { vytištění na displej (kontrola)} begin Write(Autor, ' : ', Nazev, ', ', Editor, ' ', Rok, ', '); case Medium of Kniha : Writeln('KNIHA'); MGF : Writeln('MGF'); LP : Writeln('LP'); CD : Writeln('CD') end end; Writeln; Write('vymazat ? (A/N) : '); { potvrzení požadavku výmazu } Readln(Volba); if UpCase(Volba) = 'A' then begin { výmaz : } for I := CisloTitulu to FileSize(Knihovna)-1 do { pro všechny } { další tituly } begin Read(Knihovna, T); { načtení titulu } Seek(Knihovna, I-1); { a jeho zápis na předchozí } Write(Knihovna, T); { pozici } Seek(Knihovna, I+1) { další titul } end; Seek(Knihovna, FileSize(Knihovna)-1); { "odříznutí" } Truncate(Knihovna) { poslední komponenty } end end end; { VymazTitulu } procedure SeznamTitulu; {---------------------} var Seznam : Text; { soubor výpisu seznamu } Spec : string; { specifikace souboru } T : Titul; { vypisovaný titul } CisloTitulu : LongInt; { pořadí titulu v knihovně } begin { SeznamTitulu } Write('seznam vytisknout do souboru : '); { otevření seznamu } Readln(Spec); Assign(Seznam, Spec); Rewrite(Seznam); Writeln(Seznam); Writeln(Seznam, 'knihovna : ', JmenoKnihovny); { zápis jména } Writeln(Seznam); { knihovny } Writeln(Seznam, ' ':8, 'autor', ' ':18, { a hlavičky seznamu } 'název', ' ':18, 'editor', ' ':7, 'rok', ' ':4, 'médium'); Writeln(Seznam); Seek(Knihovna, 0); { od začátku knihovny} for CisloTitulu := 1 to FileSize(Knihovna) do { pro všechny tituly } begin Read(Knihovna, T); { načtení titulu } with T do { z knihovny } begin Write(Seznam, CisloTitulu:5, ' ':3, { výpis položek } Autor, ' ':23-Length(Autor), { titulu do seznamu } Nazev, ' ':23-Length(Nazev), Editor, ' ':13-Length(Editor), Rok, ' ':3); case Medium of Kniha : Writeln(Seznam, 'KNIHA'); MGF : Writeln(Seznam, 'MGF'); LP : Writeln(Seznam, 'LP'); CD : Writeln(Seznam, 'CD') end end end; Close(Seznam) { zavření seznamu } end; { SeznamTitulu } begin { program } Writeln; { tisk titulku } Writeln('KNIHOVNÍK'); Writeln; JmenoKnihovny := 'NUL'; { inicializace } Assign(Knihovna, JmenoKnihovny); { knihovny } Reset(Knihovna); repeat Writeln('aktuální knihovna: ', JmenoKnihovny); { tisk specifikace } Writeln; { aktuální knihovny } Writeln('Z .......... založení knihovny'); { a menu operací nad } Writeln('O .......... otevření knihovny'); { knihovnou } Writeln('P .......... přípis titulu'); Writeln('V .......... výmaz titulu'); Writeln('S .......... seznam titulů'); Writeln('K .......... konec'); Writeln; Write('akce: '); Readln(Akce); { volba } Writeln; Akce := UpCase(Akce); case Akce of { výběr odpovídající } 'Z': ZalozeniKnihovny; { procedury } 'O': OtevreniKnihovny; 'P': PripisTitulu; 'V': VymazTitulu; 'S': SeznamTitulu; 'K': else Writeln(#7'neznámá akce') end; Writeln until Akce = 'K'; { dokud není zvolenou} { akcí konec } Close(Knihovna) { zavření knihovny } end. { program }
IX.5. |
Netypové soubory |
Otevření netypového souboru K otevření netypového souboru lze opět (jako k otevření souboru typového) použít pouze proceduru Reset nebo Rewrite.
procedure Reset (var F: file [; Velikost: Word]);
procedure Rewrite (var F: file [; Velikost: Word]);
Činnost obou procedur je při otvírání netypových souborů obdobná jako při otvírání souborů typových, je však rozšířena o definici velikosti komponent souboru — počtu bytů základního přenosového bloku. Pokud nepovinný parametr Velikost není uveden, je velikost přenosového bloku nastavena na implicitní hodnotu 128 bytů.
Zpracování netypového souboru Pro index-sekvenční zpracování netypových souborů lze použít jednak podprodprogramy FileSize, FilePos, EOF, Seek a Truncate, jejichž funkce byla popsána již u souborů typových, jednak speciální procedury BlockWrite (zápis bloku do souboru) a BlockRead (čtení bloku ze souboru).
procedure BlockWrite (var F: file; var Prom; Pocet: Word
[; var SkutPocet : Word]);
procedure BlockRead (var F: file; var Prom; Pocet: Word
[; var SkutPocet: Word]);
Skutečným parametrem, odpovídajícím formálnímu parametru Prom, smí být proměnná libovolného typu. Procedura BlockWrite zapíše souvislý blok bytů z proměnné Prom do souboru F, procedura BlockRead jej do proměnné ze souboru načte. Počátkem bloku je počáteční byte proměnné resp. počáteční byte komponenty na aktuální pozici v souboru. Požadovaná velikost bloku je dána součinem počtu přenášených komponent (parametr Pocet) a jejich velikosti (definované při otevření souboru).
Po skončení přenosu je v parametru SkutPocet vrácen skutečný počet přenesených komponent. Při hodnotě menší než Pocet byl přenos předčasně ukončen, například z důvodu zaplnění média (BlockWrite) nebo vyčerpání obsahu souboru (BlockRead). Celkový počet skutečně přenesených bytů je pak dán součinem skutečného počtu přenesených komponent a jejich velikosti. Pokud nepovinný parametr SkutPocet není uveden, nastává při předčasném ukončení přenosu chyba.
Požadovaná velikost přenosového bloku by neměla překročit hranici 65 535 bytů. Při jejím překročení totiž sice není generována chyba, ale velikost přeneseného bloku neodpovídá očekávání:
Tento program založí v aktuálním adresáři soubor zkusebni.sou, při jeho otevření definuje velikost komponent na 6 000 bytů a pokusí se do něj zapsat blok patnácti komponent, alokovaný v operační paměti na adrese proměnné Buffer. Při výpočtu velikosti přenosového bloku dojde k přetečení, takže výsledkem nebude 15 × 6 000 = 90 000, nýbrž 15 × 6 000 mod 65 536 = 24 464 (nejnižších šestnáct bitů matematické hodnoty součinu). Do souboru tedy bude zapsáno právě 24 464 bytů (jak se lze přesvědčit otestováním jeho velikosti) — 6 bytů proměnné Buffer, další dva byty proměnné SkutPocet a dalších 24 456 bytů. Programem hlášený počet přenesených bloků je však 4 a jemu odpovídající hlášená velikost přeneseného bloku 24 000 bytů.var F : file; Buffer : Real; SkutPocet : Word; begin Assign (F, 'zkusebni.sou'); Rewrite (F, 6000); BlockWrite (F, Buffer, 15, SkutPocet); Close (F); Writeln ('přeneseno bloků: ', SkutPocet); Writeln ('přeneseno bytů : ', 6000 * SkutPocet) end.
{======================================================================} program Kopie1; {======================================================================} const VelBuf = 64000; { velikost bufferu } var Zdroj, { kopírovaný soubor } Cil : file; { kopie } Spec : string; { specifikace souboru } Buf : array [1..VelBuf] of Byte; { přenosový buffer } Pocet, { počet úseků o } { velikosti VelBuf } Zbytek, { velikost zbytku } I : Word; { pomocná proměnná } begin {program} Writeln; { tisk titulku } Writeln('KOPIE 1'); Writeln; Write('zdroj : '); { inicializace souboru Zdroj } Readln(Spec); Assign(Zdroj, Spec); Reset(Zdroj, 1); Write('cíl : '); { inicializace souboru Cil } Readln(Spec); Assign(Cil, Spec); Rewrite(Cil, 1); Pocet := FileSize(Zdroj) div VelBuf;{ počet jednorázově kopírovaných } { úseků } for I := 1 to Pocet do { kopírování po úsecích o } begin { velikosti bufferu } BlockRead (Zdroj, Buf, VelBuf); { načtení bufferu ze Zdroj } BlockWrite(Cil, Buf, VelBuf) { výpis bufferu do Cil } end; Zbytek := FileSize(Zdroj) { velikost nezkopírovaného zbytku } mod VelBuf; BlockRead (Zdroj, Buf, Zbytek); { načtení zbytku do bufferu } BlockWrite(Cil, Buf, Zbytek); { výpis zbytku do Cil } Close(Zdroj); { zavření souborů } Close(Cil) end. { program } {======================================================================} program Kopie2; {======================================================================} var Zdroj, { kopírovaný soubor } Cil : file of Byte; { kopie } Spec : string; { specifikace souboru } Buf : Byte; { přenosový buffer } I : LongInt; { pomocná proměnná } begin { program } Writeln; { tisk záhlaví } Writeln('KOPIE 2'); Writeln; Write('zdroj : '); { inicializace souboru Zdroj } Readln(Spec); Assign(Zdroj, Spec); Reset(Zdroj); Write('cil : '); { inicializace souboru Cil } Readln(Spec); Assign(Cil, Spec); Rewrite(Cil); for I := 1 to FileSize(Zdroj) do { kopírování souboru } begin Read (Zdroj, Buf); { načtení bytu } Write(Cil, Buf) { výpis bytu } end; Close(Zdroj); { zavření souborů } Close(Cil) end. { program }
IX.6. |
Ošetření chybových stavů |
Volba mezi automatickým a uživatelským ošetřením vstupně-výstupních chyb v uvažované části programu se provádí již při překladu příslušného úseku zdrojového textu, uvedením přepínačové direktivy {$I} na jeho začátku. Ve stavu {$I+} je povoleno automatické ošetření vstupně-výstupních chyb — důsledkem výskytu vstupně-výstupní chyby je běhová chyba programu. Ve stavu {$I–} je automatické ošetření vstupně-výstupních chyb zakázáno a předpokládá se, že budou ošetřeny uživatelsky — při výskytu chyby je pouze uloženo její číslo do standardní proměnné InOutRes. Program pak běží dál, avšak veškeré vstupně-výstupní operace jsou ignorovány, dokud není hodnota proměnné InOutRes vynulována.
Obsahuje chybový kód naposled provedené vstupně-výstupní operace — nulu, pokud byla úspěšná, resp. kladné číslo vzniklé chyby.
Seznam chybových kódů je poměrně rozsáhlý a je součástí jak manuálu, tak interaktivní nápovědy ve vývojovém prostředí. Nejčastěji se vyskytují chyby:
kód | popis chyby | |||||||
---|---|---|---|---|---|---|---|---|
0 | žádná chyba, úspěch | |||||||
2 | soubor nenalezen | |||||||
3 | vadná či neexistující cesta | |||||||
5 | přístup zakázán (například při pokusu otevřít pro zápis soubor, který je přístupný pouze pro čtení) | |||||||
106 | chybný číselný formát (při čtení čísla z textového souboru) |
Vrací aktuální hodnotu proměnné InOutRes (chybový kód poslední vstupně-výstupní operace) a současně ji vynuluje (přiřadí proměnné hodnotu nula), čímž umožní normální funkci vstupně-výstupních operací.
var F : Text; { ovládací proměnná souboru } Jmeno : string; { specifikace fyzického souboru } Chyba : Word; { chybový kód } ... {$I-} { manuální ošetření IO chyb } repeat { opakuj } Write ('jméno souboru : '); { načtení systémové specifikace } Readln (Jmeno); { fyzického souboru } Assign (F, Jmeno); { spojení F s fyzickým souborem } Reset (F); { pokus o otevření pro čtení } Chyba := IOResult; { výsledek pokusu } if Chyba <> 0 then { nebyl-li pokus úspěšný } Writeln ('chyba ', Chyba, { zpráva o chybě } ' při otvírání souboru ', Jmeno) until Chyba = 0; { dokud není soubor otevřen } {$I+} { dále již automatické ošetření } ... { IO chyb }