IX. Karakterláncok kezelése

Mennyi az annyi?

A karakterláncok kezelése közben az egyik leggyakrabban használt információ a karakterlánc hossza. A REXX-ben ennek a megállapítására a LENGTH függvényt használhatjuk, amelynek visszatérési értéke a paraméterként megadott karakterlánc hossza:

hossz = LENGTH('A net.Times az OS/2 Times utoda')

A példában megadott hossz változó értéke 31 lesz a szerkezet kiértékelése után, mivel a REXX a szóközöket is beszámolja. A WORDS utasítással megszámolhatjuk a karakterláncban található szavakat. Ha a fenti példát a következôképpen módosítjuk, akkor viszont 6 lesz a hossz változó értéke:

hossz = WORDS('A net.Times az OS/2 Times utoda')

Rendelkezésre áll még a WORDLENGTH függvény is, amellyel egy karakterláncban található szó hosszát kérdezhetjük le:

/* Kinyomtatja a karakterlánc második szavának hosszát (9). */
SAY WORDLENGTH('A net.Times az OS/2 Times utoda', 2) 

Karakterláncok alakítása

Az adatkonverzió, ezen belül pedig a szövegkonverzió az egyik leggyakrabban megvalósított programfunkció. A REXX nyelv nagyon alkalmas erre a feladatra, s ezt a jó tulajdonságát többek között a sokoldalú TRANSLATE funkciónak is köszönheti. Az alapértelmezés szerint a TRANSLATE nagybetûs alakra formálja át a paraméterként beadott karakterláncot, azonban megfelelôen definiált konverziós táblázatokkal ennél sokkal többre is képes. A szintakszis a következô:

TRANSLATE( karakterlánc [, kimeneti_táblázat, bemeneti_táblázat, pótkarakter])

A bemeneti és kimeneti táblázatok írják le, hogy mely karaktereket és mire akarjuk transzformálni. Ha a bemeneti táblázat ugyanazt a karaktert kétszer is tartalmazza, akkor a karakter elsô elôfordulása számít. Amikor mindkét tábla hiányzik, a nagybetûs konverzió történik. Ha csak a bemeneti tábla hiányzik, akkor az ugyanazt jelenti, mintha a bemeneti táblának az összes karaktert megadtuk volna. Amennyiben a kimeneti tábla hiányzik, akkor az egy üres karakterlánccal lesz helyettesítve és szükség esetén megtoldódik egy, a pótkarakterekbôl álló karakterlánccal. A pótkarakter alapértelmezés szerinti értéke a szóköz. Lássunk néhány példát a TRANSLATE használatára:

/* Nagybetûs konverzió */
SAY TRANSLATE('A net.Times az OS/2 Times utoda') 

/* A szóközöket kicseréljük kötôjelre */
SAY TRANSLATE('A net.Times az OS/2 Times utoda', '-', ' ') 

/* A jelszó helyett csillagokat jelenítünk meg */
SAY TRANSLATE('password', , , '*') 

A ki és bemeneti táblázatok készítésére nagyon alkalmas az XRANGE függvény, amely egy karakterláncot tölt fel:

/* Az összes nagybetû */
input = XRANGE('A', 'Z')
/* Az összes kisbetû */
output = XRANGE('a', 'z')

Gyakran találkozunk olyan karakterláncokkal, amelyek szóköz karakterekkel kezdôdnek, vagy végzôdnek. A STRIP függvénnyel eltávolíthatjuk ôket, ha zavaróak:

SAY STRIP('   abcd    ', 'Leading')  /* Eredmény: 'abcd    ' */
SAY STRIP('   abcd    ', 'Trailing') /* Eredmény: '    abcd' */
SAY STRIP('   abcd    ', 'Both')     /* Eredmény: 'abcd'     */

A Leading, Trailing és Both paramétereknek elég csak a kezdôbetûjét megadni. Ha nem adunk meg semmit, akkor az alapértelmezés szerinti érték a B (Both) lép életbe. A szóközön kívül más karaktereket is levághatunk a karakterlánc elejérôl vagy végérôl, ha a levágandó karaktert a harmadik paraméterként definiáljuk:

SAY STRIP('xxxxabcdxxxx', 'B', 'x')  /* Eredmény: 'abcd'     */

Sokkal ritkábban van szükségünk a REVERSE függvényre, amely tükrözve adja vissza a bemeneti karakterláncot. Valószínûleg sokan nem is tudják elképzelni, hogy erre a függvényre szükség lehet, pedig nagyon jól jön akkor, ha pl. egy karakterlánc utolsó karaktereit kell manipulálni.

Decimális és hexadecimális konverzió

A számítógépek többsége numerikus formában tárolja a karaktereket. A legtöbb PC-n az ASCII (American Standard Code for Information Interchange) kódolás használatos. Bármilyen ASCII karaktert megjeleníthetünk, ha lenyomva tartjuk az ALT billentyût és a numerikus billentyûk segítségével begépeljük a karakter ASCII kódját. Az ALT-65 bevitele pl. az A betût jeleníti meg. Jó néhány beépített REXX függvény létezik, melyekkel a karakterek és kódok közötti átalakítás elvégezhetô. Talán a leggyakrabban használt konverzió a D2C, amely segítségével tizes számrendszerû kódokat alakíthatunk karakterekké. A Hello szót pl. az alábbi bonyolult módon is kiírathatjuk a D2C felhasználásával:

SAY D2C(72) D2C(101) D2C(108) D2C(108) D2C(111)

A fordított irányú konverzióra a C2D függvény használható. Hasonló a funkciót valósítanak meg az X2C és C2X függvények, csak ezek éppen hexadecimális (16-os számrendszer alapú) kódok alapján mûködnek. Az alábbi táblázatban összefoglaltuk a karakterkonverziós függvényeket.

Konverzió:Függvény:Példa:Eredmény:
Karakter decimális kóddáC2DC2D('a')97
Decimális kód karakterréD2CD2C(97)a
Karakter hexakóddáC2XC2X('a')61
Hexakód karakterréX2CX2C('61')a

A következô program segítségével megtudhatjuk a begépelt karakterek decimális és hexadecimális kódját:

/* Példaprogam a kódok visszafejtésére */
PARSE ARG karakter
IF karakter = '' THEN 
	DO
		SAY 'Elfelejtetted begépelni a karaktert!'
		EXIT
	END
SAY 'A(z) 'karakter' decimális és hexadecimális kódjai:' C2D(karakter) C2X(karakter)
EXIT

Karakterláncok összeadása

Minden kicsit is komolyabb REXX programban szükség lehet karakterláncok összeadására. Erre több lehetôség is van a REXX-ben. A legegyszerûbbet már tanultuk. Ha két karakterlánc egymás mellett áll egy szerkezetben, akkor a REXX ezeket automatikusan összeadja, azonban a két karakterlánc közé betesz egy szóközt is:

l1 = 'Helló' 'világ'
l2 = 'Helló'    'világ'
SAY l1	/* Eredmény: Helló világ */
SAY l2	/* Eredmény: Helló világ */

Ha a két karakterláncot úgy akarjuk összeadni, hogy ne legyen közöttük szóköz, akkor használhatjuk az illesztési technikát vagy a || operátort. Az illesztési technika egyszerûen azt jelenti, hogy a karakterláncokat közvetlenül egymás mellé kell helyezni, hogy szóköz nélkül toldja ôket a REXX egybe. Mivel az értelmezônek fel kell ismernie, hogy két eredetileg különálló karakterláncot akarunk egybeolvasztani, ez a konstrukció nem használható azonos formában megadott karakterláncok összeadására. Ilyen esetben a || operátor segítségével lehet a problémát áthidalni. Most pedig lássunk néhány példát:

Összetoldás:Eredmény:
'net.' 'Times'net. Times
'net.' 'Times'net. Times
'net.''Times'net.'Times
'net.' || 'Times'net.Times
xxx = 'net.'; xxx'Timesnet.Times
xxx = 'net.'; yyy = 'Times; xxxyyyXXXYYY
(1 || 2) / 34

A relációs operátorok és a karakterláncok

Amikor a REXX értelmezô két karakterláncot hasonlít össze, akkor elhagyja a karakterláncokból az esetlegesen elôforduló kezdô szóközöket, majd a rövidebb karakterláncot kipótolja szóközökkel, és csak ezután hajtja végre az összehasonlítást karakterenként, balról jobbra haladva. Ez azt jelenti pl. hogy a 'Times ' = ' Times' összehasonlítás értéke igaz. A nagy és kisbetûk természetesen különbözônek számítanak, így pl. egy változó értékének ellenôrzése esetén erre külön figyelni kell:

IF valasz = 'OK' | valasz = 'ok' | valasz = 'Ok' | valasz = 'oK' THEN

Ez persze egyszerûben is megoldható az elôbb tanult TRANSLATE függvénnyel:

IF TRANSLATE(valasz) = 'OK' THEN

Ha pontos összehasonlításra van igényünk, akkor azt a szigorú összehasonlítási operátorokkal tehetjük meg, amelyeket közönséges megduplázással nyerünk a közönséges összehasonlítási operátorokból:

Operátor:Jele:Példa:
szigorúan egyenlô==a == b
szigorúan nem egyenlô\==, ª==a \== b
szigorúan nagyobb>>a >> b
szigorúan kisebb<<a << b
szigorúan nagyobb vagy egyenlô>>=a >>= b
szigorúan kisebb vagy egyenlô<<=a <<= b
nem szigorúan nagyobb\>>, ª>>a ª>> b
nem szigorúan kisebb\<<, ª<<a ª<< b

Természetesen az összetoldáshoz és a szigorú összehasonlításhoz használt operátorok is elhelyezhetôk az operátorok kiértékelési sorrendjét tartalmazó táblázatban, amelyik ezután válik teljessé:

Operátor csoportPéldákSorrend
unáris operátorok- + \ ªLegelsô
hatványozás**
szorzás, osztás* / % //
összeadás, kivonás+ -
összetoldás|| illesztés
összehasonlítás= <> \> >= == \== \>> <<=
logikai és&
vagy, kizáró vagy| &&Legutolsó

Karakterláncok feldarabolása

Az összetoldás ellentéte a feldarabolás. Erre a feladatra is jó néhány függvény létezik a REXX-ben, amelyek két fô csoportba oszthatóak. Az elsô csoportba azok a függvények tartoznak, amelyekkel karakter alapon lehet a darabolást elvégezni. A SUBSTR függvénnyel például a karakterpozíciók megadásával lehet a bemeneti karakterláncból egy részt kivágni:

SAY SUBSTR('Az OS/2 Times utóda a net.Times', 23, 3)

A kiértékelés után a 'net' szó fog megjelenni a képernyôn, mivel a 23 3 paraméterekkel azt kérjük a SUBSTR-tôl, hogy vágja ki a bemeneti karakterláncból a 23. karakternél kezdôdô részt 3 karakter hosszan. Ha a második paramétert elhagyjuk, akkor a kimeneti karakterlánc a 23. karaktertôl az eredeti lánc végéig fog terjedni (net.Times). Ha a kimeneti lánc hosszát meghatározó érték nagyobb, mint amennyi a bemeneti láncból kitelik, akkor a maradék hely pótkarakterrel töltôdik fel. Az alapértelmezés szerinti pótkarakter a szóköz, de ha másra van szükségünk, akkor azt megadhatjuk a parancs negyedik paramétereként:

SAY SUBSTR('Az OS/2 Times utóda a net.Times', 23, 20, '!')
/* Eredmény: net.Times!!!!!!!!!!! */

A SUBSTR közeli rokonai a LEFT és RIGHT függvények. Ezek is a bemeneti karakterláncokat darabolják, azonban automatikusan a lánc bal (LEFT), vagy pedig jobb (RIGHT) oldalából indulnak ki. A második paraméterként megadott szám a kimeneti lánc hosszát szabályozza, s az eredmény szükség esetén kiegészülhet pótkarakterekkel is:

SAY LEFT('Az OS/2 Times utóda a net.Times', 20)
/* Eredmény: Az OS/2 Times utóda */
SAY RIGHT('Az OS/2 Times utóda a net.Times', 11)
/* Eredmény: a net.Times */

A szavakon alapuló daraboló függvények egyike a SUBWORD. Mûködése nagyon hasonlít a SUBSTR-éhez, csak itt éppen a pozíciószámok nem a karakterek, hanem a bemeneti karakterláncban található szavak pozícióját illetve darabszámát jelentik:

SAY SUBWORD('Az OS/2 Times utóda a net.Times', 1, 4)
/* Eredmény: Az OS/2 Times utóda */
SAY SUBWORD('Az OS/2 Times utóda a net.Times', 5)
/* Eredmény: a net.Times */

Egy másik, ugyanebbe a csoportba tartozó függvény a WORD, amely a második paraméterként megadott pozíciójú szót adja vissza. Ez tulajdonképpen a SUBWORD egyik speciális esete:

SAY WORD('tere fere csere bere', 3)       /* Eredmény: csere */
SAY SUBWORD('tere fere csere bere', 3, 1) /* Eredmény: csere */

Keresés

Jogosan vetôdik fel a fenti függvények kapcsán, hogy mi van akkor, ha nem tudjuk a pozícióját a kérdéses láncrészletnek vagy szónak. Ebben az esetben keresnünk kell a karakterláncban, és erre kiválóan megfelelnek a POS és LASTPOS függvények. A POS függvény balról jobbra, a LASTPOS pedig jobbról balra haladva keres és találat esetén a felfedezett karakter pozícióját adja vissza:

/* Az elsô \ pozíciója a c:\system\config.sys karakterláncban */
POS('\', 'c:\system\config.sys')	/* Eredmény: 3 */
/* Az utolsó \ pozíciója a c:\system\config.sys karakterláncban */
LASTPOS('\', 'c:\system\config.sys')	/* Eredmény: 10 */

Egy opcionálisan megadható paraméterrel azt is szabályozhatjuk, hogy hányadik karaktertôl kezdôdjön a keresés:

POS('\', 'c:\system\config.sys', 4) 	/* Eredmény: 10 */

Egy az egyben így mûködik a WORDPOS függvény is, csak ez éppen a szavak pozíciójával foglalkozik:

/* Keressük Shakespeare szövegében a 'be' szót a 3. szótól kezdve */
WORDPOS('be', 'To be or not to be', 3)	/* Eredmény: 6 */

Karakterláncok kielemzése

A karakterláncok manipulálásának egyik leghatékonyabb eszköze a PARSE utasítás. A 7. leckében már volt szó a PARSE ARG és a PARSE PULL utasításokról. Most a PARSE VALUE és a PARSE VAR utasításokat ismerjük meg, amelyekkel egyetlen lépésben lehet elvégezni ugyanazt, amihez több SUBSTR utasításra lenne szükség. A PARSE VAR segítségével egy változó tartalmát értékeljük ki egy minta alapján és az eredmény a minta részét képezô változókban kerül eltárolásra:

PARSE [UPPER] VAR változó minta

A PARSE VALUE egy kifejezés eredményét értékeli ki:

PARSE [UPPER] VALUE kifejezés WITH minta

A PARSE VAR esetében a változó, a PARSE VALUE esetében pedig a kifejezést nevezzük a forrás-karakterláncnak. A PARSE utasítás a forrásláncot darabolja fel és rendeli hozzá a mintában megadott változókhoz. A darabolás legegyszerûbb esete, amikor a minta csak a változókat tartalmazza. Ekkor a PARSE a forrásláncot a szóközkarakterek mentén darabolja fel és rendeli hozzá a változókhoz. A mintában azonban szerepelhetnek általunk megadott karakterlánc-részletek is, amelyekkel a darabolást befolyásolhatjuk. Ebben az esetben a PARSE a megadott láncrészletek mentén darabolja a forrásláncot. A mintában pozíciószámokat is megadhatunk. Ekkor a pozíciók alapján fog történni a darabolás. Lássunk néhány példát:

file = 'C:\OS2\BACKUP.EXE'
PARSE VAR file drive ':\' path '\' filename '.' extension

Ebben az esetben a file változó értékét értékeljük ki és daraboljuk fel a mintakarakterek (:\, \ és .) mentén. A végeredmény a drive, path filename és extension változókban található meg és a következô értékeket tartalmazza:

drive = 'C'
path  = 'OS2'
filename = 'BACKUP'
extension = 'EXE'

Vegyük észre, hogy a darabolást meghatározó mintakarakterek nem szerepelnek a végeredményben! A mintakarakterek és szóközök mentén történô szétválasztást kombinálni is lehet:

name = 'Gates, Bill IX'
PARSE VAR name lastname ',' firstname number
/* Eredmény: lastname = Gates 		*/
/* firstname = Bill, number = IX 	*/

Ha több változó szerepel a mintában, mint amennyi részre a forrásláncot daraboljuk, akkor a felesleges változók értéke üres karakterlánc lesz. Ha több darab keletkezik, mint ahány változó van, akkor az utolsó változó fogja tárolni a maradékot. Ha ezt nem akarjuk, vagy pedig ki akarunk hagyni egy részdarabot, akkor azt a mintában egy pont megadásával jelezhetjük:

name = 'Gates, Bill IX'
PARSE VAR name lastname ',' firstname .
/* Eredmény: lastname = Gates, firstname = Bill */

Most pedig lássunk egy példát a pozíciók alapján történô darabolásra:

PARSE VALUE 'BillGates' WITH szo1 5 szo2

Ebben az esetben az 5. pozíció elôtt álló rész a szo1-be, az az utáni rész pedig a szo2-be kerül, tehát szo1 = Bill és szo2 = Gates lesz a végeredmény. Egynél több pozíciószámot is megadhatunk a mintaváltozók között, s ezzel azt is meghatározhatjuk, hogy hol kezdôdjön a következô karakterlánc-részlet. Ez arra is lehetôséget ad, hogy visszaugorjunk a forrásláncban! Az alábbi példa pl. a Red Rose szöveget jeleníti meg, igaz, kissé fura módon:

PARSE VALUE 'Rosebud' WITH first 5 1 second 2 4 third 5 7 fourth
SAY second || third || fourth first	/* Eredmény: Red Rose */

A pozíciószámok relatívek is lehetnek; a + - jelekkel a jobbra illetve a balra pozícionálást jelöljük:

PARSE VALUE 'Rosebud' WITH first +4 -4 second +1 +2 third +1 +2 fourth
SAY second || third || fourth first 	/* Eredmény: Red Rose */

A PARSE VAR és VALUE utasítások esetében megismert mintaszabályok a PARSE PULL és ARG utasítások esetében is ugyanígy mûködnek.


REXX GYÍK:

K1. Hogyan jeleníthetem meg egy karakter bináris ábrázolását?
V1. Ez két lépésben történhet. Elsô lépésben hexadecimálisra kell konvertálni a karaktert a C2X függvénnyel. Az eredményül kapott hexa értéket aztán binárissá alakíthatjuk az X2B függvénnyel.


Gyakorlatok:

1. Mi az eredmény?
a. 'moon' || 'light'
b. val = 'hope'; val'chest'
c. 'This' 'time' 'tomorrow'
d. "This time " || 'tomorrow'

2. Készítsen egy programot, amely a bevitt decimális kód alapján megjeleníti a kódhoz tartozó karaktert!

Kádár Zsolt
1998. 03. 15.
[ Elôzô lecke | Következô lecke | Tartalom ]