Chování procedur Read a ReadLn
[Pro jistotu: není potřeba (alespoň v tomto rozsahu, na základní úrovni
samozřejmě ano) umět ani ke zkoušce ani k průběžné písemce. Spíš to pro
někoho může být užitečná informace.]
POZOR: následující informace jsou testovány v Borland Pascalu. Je
možné, že v některých okrajových detailech bude chování ve Free Pascalu
odlišné. Nemám aktuálně čas to všechno znovu zkoušet, takže to nechávám, jak
to je, pokud mě někdo upozorní na jakékoli nesrovnalosti, budu samozřejmě
jen rád.
Třebaže jde o základní procedury Pascalu, jejich chování možná není ve všech
případech ani zcela intuitivní ani dokumentované a asi si zaslouží pár slov.
Následující poznámky se vesměs vztahují k procedurám Read a
ReadLn bez prvního parametru typu soubor, tedy čtoucím ze
standardního vstupu. Při čtení z textových souborů se ovšem tyto procedury
chovají v podstatě stejně.
Rozhodně netvrdím, že vše následující pochopí (nebo by měl pochopit) i ten,
kdo s Pascalem teprve začíná. Účelem této stránky je spíše upozornit na
možné problémy při použití procedur Read a ReadLn, v
případě potíží snad tyto informace pomohou k nalezení a odstranění chyby
(pokud by se v nich ani pak někdo nezorientoval, nechť mě kontaktuje).
Naprostá většina použití těchto procedur bude ovšem jistě bezproblémová,
protože většinou se chovají tak, jak člověk intuitivně očekává.
- Nejprve je potřeba učinit poznámku k termínu konec řádku. Jeho
význam může být závislý na použitém operačním systému. Na systémech od firmy
Microsoft (DOS/Windows) jde (z hlediska Pascalu) o znak #13
("Carriage return") případně (v naprosté většině případů) následovaný znakem
#10 ("Line feed") [#13 je znak s ASCII hodnotou 13,
#10 analogicky]. Na systémech z rodiny Unixů je to většinou jen
znak #10. Na ještě jiných systémech (například Mac OS) konec řádku
vyznačuje jen znak #13. Dále se bude předpokládat, že se pohybujeme
v DOSu/Windows a znak konce řádku je tedy ve skutečnosti reprezentován až
dvěma znaky.
- Bude-li se dále mluvit o interaktivní práci s programem, je tím
myšleno jeho spuštění v rámci IDE (Ctrl+F9, nejčastější varianta),
případně jeho spuštění z příkazové řádky systému. V obou případech
uživatel/programátor s programem komunikuje s pomocí klávesnice. Druhou,
odlišovanou možností bude spuštění programu z příkazové řádky s
přesměrovaným vstupem z nějakého souboru (např. příkazem program <
soubor), v kterémžto případě se z klávesnice nic nečte.
- Další poznámka je spíše okrajová a jen pro úplnost. Kromě konce řádku je
významným znakem i konec souboru. Za tento je považován buďto znak
#26 (z klávesnice jde zadat jako Ctrl+Z, pak je ovšem
ještě nutné zmáčknout Enter), nebo fyzický konec souboru (není
možné vyvolat interaktivně, může k tomu dojít jen při přesměrování nějakého
souboru na vstup programu). Netuším, jak něco takového funguje u překladačů
Pascalu na Unixech nebo Mac OS.
- Celé čtení ze vstupu procedurami Read a ReadLn je
možná vhodné představit si jako rouru. Při interaktivní práci s programem
jsou z jedné strany strkána písmenka, která uživatel mačká na klávesnici. Je
ovšem důležité si uvědomit, že se v rouře neobjevují hned po stisku
příslušné klávesy, ale až po stisku klávesy Enter, kdy se v rouře
objeví celý "řádek" znaků předcházejících stisku klávesy Enter a za
nimi navíc znak konce řádku. Pokud je na vstup programu (např. na příkazové
řádce) přesměrován nějaký soubor, je do naší pomyslné roury vložen celý a
další chování je stejné, jako by na jeho konec byl navíc přidán znak konce
souboru.
Ať už je roura plněna jakkoli, čtení popisovanými procedurami si lze
představit jako vybírání znaků z druhého konce této roury. Každé volání
některé z procedur vybere žádný, jeden nebo několik znaků. Je zřejmé, že se
tím roura nemusí vyprázdnit celá, v takovém případě zbytek jejího obsahu
čeká na "zpracování" dalšími příkazy Read či ReadLn.
Naopak se ale (ovšem pouze při interaktivní komunikaci s programem) může
stát, že se roura vyprázdní, aniž by bylo čtení dokočeno (např. čteme do
proměnné číselného typu a uživatel zatvrzele zadává jen bílé znaky, viz
dále). V takovém případě čtení pokračuje a příkaz Read či
ReadLn zpracovává případný nový obsah roury do té doby, než
uživatel zadá vhodné znaky.
- Argumentem procedur Read a ReadLn mohou být pouze
proměnné typu Char či celočíselných typů (případně typů intervalů
nad těmito typy, nebude dále samostatně zmiňováno) nebo proměnných typu
string či reálných typů.
- Read(X)
- Je-li X proměnná celočíselného nebo reálného typu,
Read přeskočí všechny případné bílé znaky (mezery, tabelátory a
znaky konce řádku) a následující řetězec (všechny znaky až do prvního
dalšího bílého znaku nebo konce souboru) se pokusí převést na číslo. Pokud
řetězec neodpovídá očekávanému formátu (závisí na konkrétním typu proměnné),
nastane I/O chyba a program je ukončen. Pokud řetězec odpovídá reprezentaci
nějakého čísla, je tato hodnota přiřazena do proměnné. Pokud je řetězec
prázdný (po přeskočení bílých znaků jsme se dostali až na konec souboru), je
do proměnné přiřazena hodnota 0 (nebo nedefinovaná hodnota –
to se těžko poznává experimentálně a nápověda do takových detailů ani
zdaleka nezachází). Případné další volání procedury Read vyvolá
čtení od bílého znaku nebo konce souboru, který na vstupu následoval za
právě přečteným číslem.
- Je-li X proměnná typu string, načtou se ze vstupu
všechny znaky až po nejbližší konec řádku nebo souboru, ten zůstane na
vstupu a do proměnné se nenačte. Načte se však nejvýše tolik znaků, kolik je
maximální délka proměnné X (defaultně 255, lze nastavit při
deklaraci), pokud by před nejbližším koncem řádku nebo souboru bylo znaků
více, budou "nadbytečné" znaky ponechány na vstupu.
- Je tedy dobré si uvědomit, že například posloupnost příkazů
Read(s1); Read(s2), kde s1 a s2 jsou proměnné
typu string, nedává příliš dobrý smysl (pokud tedy nepracujeme s
řádky delšími, než je maximální délka s1), protože do prvního
řetězce se načte vše až do konce řádku (ten se ovšem nechá na vstupu) a do
druhého vždy prázdný řetězec (před koncem řádku už nic není). Do řetězcových
proměnných tedy raději načítat pomocí ReadLn, viz dále.
- Je-li X proměnná typu Char, načte se ze vstupu jeden
znak a přiřadí do proměnné.
- Pokud je na vstupu prvním znakem konec řádku načte se do proměnné znak
#13. Je potřeba si uvědomit, že na této platformě je konec řádku
většinou reprezentován dvěma byty, další Read(X) by v takovém
případě načetl znak #10 ("Line feed"). Pokud bychom po načtení
znaku #13 načítali proměnnou číselného typu, znak #10 by
byl přeskočen (jde o bílý znak), dále viz výše. Pokud bychom ovšem v takové
chvíli načítali proměnnou typu string, je důležité si uvědomit, že
samotný znak #10 nebude rozpoznán jako konec řádku a bude tedy
načten jako první znak řetězce. Další znaky řetězce budou znaky dalšího
řádku vstupu (jeho zadání bude v interaktivním režimu nutné pro další běh
programu) až po nejbližší další konec řádku či souboru, viz výše.
- Pokud je na vstupu prvním znakem konec souboru, bude načten do proměnné,
ale zůstane na vstupu. Případné další Read(X) by udělalo
opět to stejné.
- Read(X, Y) -- totéž jako begin Read(X); Read(Y);
end, obdobně pro libovolný počet parametrů.
- ReadLn (bez parametrů) -- přečte ze vstupu všechny znaky (i
případný zbytek konce řádku #10, viz výše) až do konce řádku nebo
konec souboru (v interaktivním režimu případně počká na jeho zadání), v
případě konce řádku včetně něj, naopak konec souboru na vstupu
zůstane.
- ReadLn(X) -- totéž jako begin Read(X); ReadLn;
end, pokud by tedy například X byla proměnná typu
string, načte se nejvýše tolik znaků, kolik je její maximální délka
(viz výše), případný zbytek řádku (libovolně dlouhý) se zahodí.
- ReadLn(X, Y) -- totéž jako begin Read(X); Read(Y); ReadLn;
end, obdobně pro libovolný počet parametrů.