Kimeneti és bemeneti streamek
A REXX minden bemeneti és kimeneti adatot karakterek sorozataként (stream, magyarul talán patakocskának lehetne fordítani) értelmez. A streameket aztán feldolgoztathatjuk soronként vagy pedig karakterekre bontva. Minden streameknek van egy forrás- és egy rendeltetési pontja. Már ismerjük a leggyakrabban elôforduló forráspontot (standard input, stdin), a billentyûzetet. A leggyakoribb rendeltetési pont (standard output, stdout) pedig a képernyô. A legtöbb REXX függvény alapértelmezés szerint a standard inputról, vagyis a billentyûzetrôl várja a bemeneti adatokat, a kimenetét pedig a standard outputon, azaz a képernyôn jeleníti meg.
A stream lehet egy fájl, soros port, nyomtató, vagy egy másik eszköz. A fájlokat állandó, a perifériákat pedig ideiglenes streameknek nevezzük. A fô különbség az, hogy az állandó streamekbôl az adat ismételten kiolvasható, míg az ideiglenesbôl csak egyszer. Az állandó streamek esetében az írás vagy olvasás pozíciója is megadható. Az ideiglenes streamek dinamikus voltából kifolyólag erre nincs lehetôség.
Mielôtt egy streamet használni tudnánk, meg kell nyitni, majd pedig amikor már nincs rá szükségünk, be kell zárni. A REXX programozási nyelvben a ki- és bevitelt végzô függvények automatikusan elvégzik a streamek megnyitását, így ezzel többnyire nem nagyon kell foglalkoznunk. Amikor programunk véget ér, akkor a parancsértelmezô, szintén teljesen automatikusan, lezárja a még nyitott streameket. A stdin, stdout és stderr (standard error) streamek kivételével az automatikusan megnyitott streamek írhatóak és olvashatóak. Más folyamat nem használhatja ôket, amíg nyitva vannak. Amennyiben arra van igényünk, a streameket megnyithatjuk csak írásra, illetve olvasásra is. A továbbiakban áttekintjük azokat a beépített REXX függvényeket, amelyekkel írhatjuk és olvashatjuk a streameket.
Sorok írása és olvasása
A PC-n létrehozott szövegfájlok sorokból állnak, amelyeket két speciális karakter, a kocsi vissza (carriage return) és az új sor (new line) választ el. A szöveg írására és olvasására használható függvények automatikusan lekezelik ezeket a karaktereket, így nekünk ezzel nem kell bajlódnunk.
Olvasásra a LineIn függvényt használhatjuk, amely az elsô meghíváskor a stream elsô, aztán pedig mindig az éppen soron következô sort olvassa be. A függvény szintakszisa a következô:
a_beolvasott_sor = LineIn([a stream neve] [, sor] [, számláló]])
A stream neve paraméter azonosítja be az olvasandó streamet. Ha nem adjuk meg, akkor a standard inputot fogjuk olvasni. A sor paraméter egyetlen lehetséges értéke 1, amellyel a stream elsô sorára pozícionálunk. Ha nem adunk a sornak értéket, akkor mindig a következô sor kerül beolvasásra. A számláló értéke 0 vagy 1 lehet, amelytôl függôen csak pozícionálás vagy pedig pozícionálás és sorolvasás is történik. Az alábbi táblázatban összefoglaltuk a LineIn fôbb használati módjait:
| Függvényhívás: | A függvényhívás hatása: |
| LineIn() | Beolvas egy sort a standard inputról. |
| LineIn(név) | Megnyitja a név streamet és beolvassa annak elsô sorát. Amennyiben a stream már nyitva van, akkor beolvassa annak következô sorát. |
| LineIn(név, 1, 0) | Megnyitja a név streamet és annak elsô sorára pozícionál. Olvasás egyelôre nem történik. |
Sokszor van arra is szükség, hogy az olvasás elôtt ellenôrizzük, hogy nem értük-e még el a stream végét. Ezt a Lines függvénnyel tehetjük meg:
rc = Lines([a stream neve])
A Lines függvény 0-át ad vissza, ha már elértük a stream végét. Amennyiben van még olvasható sor, a visszaadott érték 1 lesz. Lássunk is most rögtön egy példát a Lines és a LineIn függvények használatára.
/* A config.sys sorainak kiolvasása és megjelenítése */ /* Ha nem a C:-n van az OS/2, akkor írjuk át a file értékét! */ file = 'c:\config.sys' DO WHILE Lines(file) say LineIn(file) END EXIT
A LineIn függvény párja a LineOut, amellyel sorokat írhatunk streambe. Hasonlóan testvéréhez, a LineOut is automatikusan megnyitja a streamet, amennyiben az szükséges, és hozzátoldja a végéhez az általunk megadott karakterláncot:
rc = LineOut([a stream neve] [, karakterlánc] [, sor]])
A stream neve adja meg, hogy hová akarjuk íratni a második paraméterként megadott karakterláncot. Amennyiben ez az információ hiányzik, a LineOut függvény a standard outputra írja ki a sort. A sor paraméter értéke csak 1 lehet, ha megadjuk. Ezzel azt jelezzük, hogy nem a stream végére, hanem annak elejére akarunk írni. Ha csak a stream nevét adjuk meg, akkor a LineOut lezárja a streamet. Az alábbi táblázatban összefoglaltuk a fôbb használati formákat:
| Függvényhívás: | A függvényhívás hatása: |
| LineOut( , szöveg ) | Kiírja a szöveget a standard outputra. |
| LineOut(név, szöveg) | Megnyitja a név streamet és hozzátoldja a szöveget a végéhez. |
| LineOut(név, szöveg, 1) | Megnyitja a név streamet és az elejére írja a szöveget. |
| LineOut(név) | Lezárja a név streamet. |
| LineOut(név, ,1) | Megnyitja a név streamet és az elsô sorra pozícionál. Írás nem történik. |
Most pedig vessünk egy pillantást leckénk második példaprogramjára, amely megtisztítja a config.sys PATH változóit a már nem létezô alkönyvtáraktól!
/* A PATH-ok megtisztítása. Ha nem a C:-n van az OS/2, akkor írjuk át a */ /* config és ujconf változók értékét! */ config = 'c:\config.sys' ujconf = 'c:\config.new' 'del 'ujconf' > NUL' CALL SetLocal DO WHILE Lines(config) sor = Strip( LineIn(config), 'B') SELECT WHEN Left(sor, 9) = 'SET PATH=' THEN DO SAY '' SAY 'Most tisztítom a PATH-t...' PARSE VAR sor . '=' path sor = 'SET PATH=' || tisztit(path) END WHEN Left(sor, 8) = 'LIBPATH=' THEN DO SAY '' SAY 'Most tisztítom a LIBPATH-t...' PARSE VAR sor . '=' path sor = 'LIBPATH=' || tisztit(path) END WHEN Left(sor, 10) = 'SET DPATH=' THEN DO SAY '' SAY 'Most tisztítom a DPATH-t...' PARSE VAR sor . '=' path sor = 'SET DPATH=' || tisztit(path) END OTHERWISE NOP; END CALL LineOut ujconf, sor END SAY '' SAY 'Kész!' CALL EndLocal EXIT tisztit: PROCEDURE path = Arg(1) ujpath = '' IF Left(path, 1) = ';' THEN path = Substr(path, 2) DO WHILE path <> '' PARSE VAR path konyvtar ';' path konyvtar = TRANSLATE(konyvtar) SELECT WHEN konyvtar = '' THEN ITERATE WHEN konyvtar = '.' THEN ujpath = '.;' || ujpath WHEN Directory(konyvtar) <> konyvtar THEN SAY 'Érvénytelen könyvtár: 'konyvtar OTHERWISE ujpath = ujpath || konyvtar || ';' END END RETURN ujpath
Bár a program viszonylag hosszú, a mûködése azért meglehetôsen egyszerû. A fôprogram beolvassa a config.sys sorait és ha nem a PATH, LIBPATH vagy pedig DPATH kezdetû sorokról van szó, akkor változtatás nélkül kiírja ôket a config.new fájlba. Amennyiben a tisztítandó sorok valamelyikérôl van szó, akkor meghívjuk a tisztít eljárást, amely egyenként leellenôrzi, hogy át tud-e váltani a sorban szereplô alkönyvtárakba. Ha nem, akkor a config.new megfelelô sorába már nem kerül bele a nem létezô alkönyvtár.
Karakterek írása és olvasása
Gyakran van szükségünk arra is, hogy ne sorokat, hanem karaktereket írjunk illetve olvassunk. Olvasásra kiválóan megfelel a CharIn függvény:
a_beolvasott_karakter = CharIn([a stream neve] [, kezdet] [, hossz]])
A CharIn függvény automatikusan megnyitja az olvasni kívánt streamet és beolvassa az elsô pozíción található karaktert. Ha már nyitva van a stream, akkor a soron következô karaktert adja vissza. Az olvasási pozíció és a beolvasott karakterek száma a kezdet és hossz paraméterekkel szabályozható. Az alábbi táblázatban megadtuk a leggyakoribb használati módokat.
| Függvényhívás: | A függvényhívás hatása: |
| CharIn() | Beolvas egy karaktert a standard inputról. A program végrehajtása addig szünetel, amíg a felhasználó meg nem nyomja az ENTERT-t. |
| CharIn( , , 3) | Beolvas három karaktert a standard inputról. |
| CharIn(név) | Megnyitja a név streamet és beolvassa annak elsô karakterét. Ha a stream már nyitva volt, akkor beolvassa az éppen soron következô karaktert. |
| CharIn(név, , 5) | Megnyitja a név streamet és beolvassa annak elsô öt karakterét. Ha a stream már nyitva volt, akkor beolvassa az éppen soron következô öt karaktert. |
| CharIn(név, 1, 0) | A név stream kezdetére pozícionál, olvasás nem történik. |
Karakterek olvasásakor hasznos lehet a Chars függvény is, amely azt mondja meg, hogy hány karakter van még az éppen olvasott streamben:
karakterszam = Chars([a stream neve])
Az íráshoz a CharOut függvényt lehet használni. A szintakszis a következô:
rc = CharOut([a stream neve] [, karakterlánc] [, kezdet]])
A CharOut függvény automatikusan megnyitja a streamet, amennyiben az szükséges, és kiírja a megadott karaktert vagy karaktereket a streambe a kezdet paraméterrel megadott pozícióból kiindulva. Az alábbi táblázatban most is összefoglaltuk a leggyakoribb használati formákat:
| Függvényhívás: | A függvényhívás hatása: |
| CharOut( , szöveg) | Kiírja a szöveget a standard outputra. |
| CharOut(név, szöveg) | Megnyitja a név streamet és hozzáírja a végére a szöveget. |
| CharOut(név, szöveg, 8) | Megnyitja a név streamet és a nyolcadik pozíciótól kezdôdôen kiírja a szöveget. Ha a stream kevesebb, mint nyolc karaktert tartalmaz, akkor a mûvelet nem lesz sikeres. |
| CharOut(név, , 34) | Az írás pozícióját a 34. karakterre állítja. Írás nem történik. A mûvelet sikertelen lesz, amennyiben a stream kevesebb, mint 34 karaktert tartalmaz. |
A STREAM függvény
Néha az is elôfordulhat, hogy íráson és olvasáson kívül más stream-mûveleteket (pl. megnyitás csak olvasásra, írási, olvasási pozícionálás, stb.) is szeretnénk elvégezni. A Stream függvény tulajdonképpen ezeknek a kiegészítô mûveleteknek a kelléktára:
rc = Stream(fájlnév [, mûvelet] [, parancs]])
A mûveletnek három fajtája lehet, parancs (Command, C), leírás (Description, D) vagy állapot (State, S). Amikor a mûvelet parancs, a harmadik paraméter értéke az alábbi táblázatban szereplô értékek egyike kell, hogy legyen:
| Parancs: | Magyarázat: |
| OPEN | Megnyitja a streamet írásra és olvasásra. |
| OPEN READ | Megnyitja a streamet, de csak olvasásra. |
| OPEN WRITE | Megnyitja a streamet, de csak írásra. |
| CLOSE | Bezárja a streamet. |
| SEEK = < + - offset | Az offset értékének megfelelôen pozícionálja az írási/olvasási pozíciót. A = karakter hatására az offset a stream kezdetétôl (ez az alapértelmezett beállítás is), a < hatására pedig a végétôl számít. A + a jelenlegi pozícióhoz képest elôre, a - pedig hátrafelé pozícionál. |
| QUERY EXISTS | Amennyiben a stream létezik, visszatérési értékként megkapjuk a teljes elérési útvonalát. Ha nem létezik, üres karakterlánc az eredmény. |
| QUERY SIZE | A stream bájtban kifejezett méretét adja vissza. |
| QUERY DATETIME | A stream utolsó módosításának dátumát és idôpontját adja vissza. |
Amennyiben a mûvelet állapot (S), a Stream függvény az alábbi értékek egyikét adja vissza:
| Visszatérési érték: | Jelentés: |
| ERROR | Értelmetlen stream-mûvelet. |
| NOTREADY | Bármelyik ki- vagy beviteli mûvelet eredménytelen lesz. |
| READY | A stream kész a mûveletekre. |
| UNKNOWN | Ismeretlen állapot. Legtöbbször akkor fordul elô, ha a stream nem létezik. |
A leírás mûvelet (D) ugyan azokat az értékeket adja vissza, mint az állapot, csak még azt megtoldja egy kettôsponttal és valamilyen kiegészítô információval. Ha pl. elértük az olvasott fájl végét, akkor a visszaadott érték NOTREADY:EOF (EOF = end of file, a fájl vége) lesz.
Perifériák elérése
A streamek ismertetésekor már említettük, hogy a periférikus eszközöket ideiglenes streameknek lehet tekinteni, és ezekre is alkalmazhatjuk az állandó streamekkel kapcsolatban megismert függvények egy részét. Az eszközöknek elôre definiált nevük van, amelyeket az alábbiakban felsoroltunk:
| Eszköz: | Leírás: |
| COM1, COM2 stb. | Soros port |
| CON | Képernyô kimenet és bemenet |
| KBD | Billentyûzet bemenet |
| LPT1, LPT2 stb. | Párhuzamos port |
| PRN | Nyomtató |
| STDERR | Standard error kimenet |
| STDIN | Standard bemenet |
| STDOUT | Standard kimenet |
| QUEUE | REXX queue, egy késôbbi leckében lesz róla szó |
Ha például a COM3 soros porton lévô modemre szeretnénk egy inicializáló karakterláncot kiküldeni, és figyelni, hogy az sikeresen lefut, akkor ezt a következô pár soros programmal tehetjük meg:
/* Modem inicializálása a COM3 porton */
port = 'COM3'
initstring = 'ATZ'
CALL Stream port, 'C', 'OPEN'
CALL LineOut port, initstring
starttime = Time('E')
valasz = ''
DO UNTIL valasz = 'OK'
valasz = LineIn(port)
if Time('E') - starttime > 30 THEN
DO
SAY 'A modem nem reagált 30 másodpercen belül!'
EXIT
END
END
SAY 'A modem sikeresen inicializálva!'
CALL Stream port, 'C', 'CLOSE'
EXIT
A nyomtatóra írás ettôl kicsit bonyolultabb. Elvileg közvetlenül is lehet írni a LineOut függvény segítségével a PRN eszközre, ha azonban szépen formázott szöveget akarunk nyomtatni, vagy éppen PostScript printerünk van, akkor ez az út nem járható. A legtöbb nyomtató speciális vezérlôkaraktereket használ, amelyekkel pl. lapot dobathatunk, kövér, vagy pedig dôlt betûvel írhatunk. Ezek a vezérlôkarakterek sajnos nyomtatónként változnak, így nincs más lehetôségünk, mint a nyomtató használati utasításának áttanulmányozása. A lehetôségek érzékeltetése céljából azonban megemlítjük, hogy nagyon sok printernél a lapdobás karaktere az ASCII 12 (D2C(12) vagy '0C'x). Ha tehát a CharOut utasítással ezt a karaktert küldjük a printerre, akkor az nagy valószínûséggel lapot fog dobni. PostScript nyomtatóra történô íráskor a kiíratandó szöveget be kell ágyazni a PostScript nyelv parancsai közé, különben a nyomtató egyáltalán nem fog reagálni a számára értelmezhetetlen LineOut utasításokra.
Írási és olvasási hibák lekezelése
A kimeneti és bemeneti mûveleteket megvalósító függvények bizonyos esetekben nem járnak sikerrel és az úgynevezett NOTREADY feltétel (a stream nem elérhetô) áll be. Ha programunkban erre nem figyelünk, akkor az súlyos hibák forrása lehet. A leggyakrabban az szokott elôfordulni, hogy a streamnek már elértük a végét, s ennek ellenére tovább akarunk olvasni. A következô kódrészlet azt demonstrálja, hogy hogyan léphetünk ki egy fájlmásolást végzô hurokból, amikor elértük az olvasott fájl végét:
SIGNAL ON NotReady
DO FOREVER
CALL LineOut(outfile), LineIn(infile)
END
NotReady:
SAY 'Elértük volna a fájl végét?!'
A Stream és a Condition függvények segítségével azt is megállapíthatjuk, hogy a NOTREADY feltétel miért állt be:
SIGNAL ON NotReady
DO FOREVER
CALL LineOut(outfile), LineIn(infile)
END
NotReady:
c = Condition('D')
PARSE VALUE Stream(c, 'D') WITH state ':' info
SELECT
WHEN info = 'EOF' THEN SAY 'Elértük a fájl végét!'
WHEN state = 'UNKNOWN' THEN SAY 'A(z) 'c' fájl nem létezik!'
OTHERWISE SAY 'Ismeretlen hiba!'
END
EXIT
REXX GYÍK:
K1. Hogyan lehet az STDERR streamet használni?
V1. Az STDERR egy olyan stream, amely az alapértelmezés szerint a kijelzôre csatlakozik. Amennyiben a standard outputot a felhasználó átirányította, akkor még mindig használhatjuk az STDERR streamet, hogy a kijelzôre írjunk.
K2. Mi a különbség a PULL/SAY és a LineIn/LineOut függvények használata között?
V2. A PULL és a SAY utasítások a standard inputot és outputot használják. Ha nem akarjuk, hogy a felhasználó átirányíthassa az alapértelmezett beállításokat, akkor használjuk a LineIn és LineOut függvényeket és írjunk a KBD és CON eszközökre.
Gyakorlatok:
1. Készítsen egy REXX programot, amely ugyanazt csinálja, mint a MORE parancs!
2. A következô programban hiba van. Javítsa ki!
/* hibás program */ PARSE ARG filename DO i = 1 UNTIL Lines(filename) SAY Right(i, 4) || ':' LineIn(filename, 1) END EXIT
| Kádár Zsolt 1998. 08. 25. | [ Elôzô lecke | Tartalom ] |