11. fejezet: Érettségi túlélőcsomag 1 (szövegdarabolás, konvertálás)
Az előző fejezetekből már majdnem minden eszközt megismertél, ami egy sikeres emelt szintű érettségi programozási feladatának megoldásához kell. Ebben a fejezetben megmutatom, hogy lehet egy szöveges fájlból mindenféle adatot kinyerni, és megismerkedhetsz a moduláris programozás alapegységével, a unittal.
Unitok
Egy komolyabb projekt már túl nagy ahhoz, hogy egyetlen forrásfájl tartalmazza. Ekkor a program egyes részeit (jellemzően: a különböző részfeladatokat) külön fájlokban, modulokban készítjük el. A modulok külön fordíthatóak, végül ezeket összeépítjük a főprogrammal. Ennek a fejlesztési módszernek több előnye van. Lehetővé teszi, hogy a projektet többen fejlesszék, mindenki csak a saját részfeladatára összpontosítva. Mivel a modulok külön fordíthatóak, le is lehet őket tesztelni, és az utolsó lépésben a modulok működését nem, csak az együttműködésüket kell ellenőrizni. Ezzel a tesztelés gyakran nagyon hosszú feladatát is szétoszthatjuk.
A modul neve Pascalban unit. A unitok többek között eljárások, függvények deklarációját tartalmazzák. A Free Pascal rendszer elemeit is unitokra darabolva készítették el, minél több lehetőségét használjuk fel a nyelvnek, annál több unitot építünk hozzá a főprogramunkhoz. A legfontosabb unit neve system
, ennek használata a programunkban automatikus. Ez a unit tartalmazza pl. a write
eljárás deklarációját is. Ha azonban további unitok eljárásait is használjuk a programban, azokat deklarálnunk kell. Ez a programunk legelső deklarációja (rögtön a program
sor után):
USES unitok listája;
A következő példa a konzolképernyő törlését mutatja be.
USES Crt; BEGIN writeln('Ez nem látszik.'); clrscr; writeln('Ez igen.'); readln; END.
A képernyőt a clrscr
utasítás törli, amelyet a crt
unitban deklarálnak. A crt
unit megadása nélkül a fordító ismeretlen azonosító hibajelzést ad. Az összes unit összes eljárásának leírása megtalálható a www.freepascal.org/docs-html/rtl címen.
Szövegdarabolás
A bemenő adatokat gyakran szöveges fájlban kapjuk meg, egy sorban több adattal, közöttük valamilyen elválasztó karakterrel. Ha az adatok között szöveg is van, fel kell darabolnunk a bemeneti sort az elválasztó karakterek mentén. Ehhez az strutils
unit függvényeit használjuk fel, bár írhatnánk rá saját programot is, de az tovább tartana.
USES Strutils; VAR s:string; BEGIN s:='aaa:bbb::ccc'; writeln(wordcount(s,[':'])); //3 writeln(extractword(3,s,[':'])); //ccc writeln(extractdelimited(3,s,[':'])); //üres readln; END.
A programban felbukkan egy új típus: a halmaz. A [':']
egy set of char
típusú adat, amely karakterhalmazt jelent, ebben a programban az elválasztó karakterek halmazát. Lehetett volna [':',' ',';']
is. Elválasztó karakter természetesen az adatokon belül nem lehet.
Az egymás utáni több elválasztót kétféleképpen is lehet értelmezni. Számíthatnak egynek, ekkor a 'bbb' után 'ccc' következik. De az is lehet, hogy a két ':' között egy üres szöveg szerepel adatként. Az első értelmezésben üres szöveg nem lehet adat.
A wordcount(string,elválasztók halmaza)
függvény megadja a stringben a darabok számát. Az extractword(sorszám,string,elválasztók halmaza)
megadja az adott sorszámú rész-stringet. Ennél a két függvénynél egymás utáni elválasztók egynek számítanak.
Az extractdelimited(sorszám,string,elválasztók halmaza)
az előző függvényhez hasonló, de itt üres string is lehet adat, ezért a fenti példában a harmadik darab a két ':' közötti üres string.
Típuskonverzió
Mi a teendő, ha a beolvasott sor szöveges és számadatokat is tartalmaz?
USES Strutils; VAR s:string; n:integer; BEGIN s:='aaa:12'; n:=extractdelimited(2,s,[':']); END.
A kód fordítási hibát eredményez. Ennek oka, hogy a daraboló függvény végeredménye string típusú, és a Pascal nem értelmezi számként. Ilyen esetben típuskonverziós függvényeket használunk. Ezek a függvények a sysutils
unitban vannak. A fenti program javítva:
USES Strutils,Sysutils; VAR s:string; n:integer; BEGIN s:='aaa:12'; n:=strtoint(extractdelimited(2,s,[':'])); END.
Az strtoint(szöveg) függvény eredménye integer típusú, a szöveg egésszé konvertálva. Hasonlóképpen működik az strtofloat, amelynek eredménye lebegőpontos (real) típusú. Fontos viszont, hogy a
writeln(strtofloat('12.33'));
hibás, mert a függvény tizedesvesszőnek nem a Pascal '.' karakterét, hanem az operációs rendszerben beállított karaktert használja, ami magyar Windows esetén ','. A fenti sor helyesen:
writeln(strtofloat('12,33'));
Így viszont egy adott programkód helyes működése az operációs rendszer beállításaitól függ, ami nem szerencsés. Továbbá, ha a bemeneti fájlunkban '.' a tizedespont, nem tudjuk feldolgozni. A tizedes karaktert a sysutils
által beállított decimalseparator
változó tartalmazza, amit a programunkban átállíthatunk. A bombabiztos megoldás tehát:
decimalseparator:='.'; writeln(strtofloat('12.33'));
Az inttostr és floattostr függvények számot alakítanak át stringgé. A következő sor:
writeln(floattostr(12.33));
a várttal ellentétben 12,33-at ír ki, mert a floattostr
is a decimalseparator
alapján működik.
Mi történik, ha a beolvasott szöveg nem tartalmaz érvényes számot? Ilyen esetben az strtoint
hibajelzéssel leállítja a programot. Használhatjuk ilyenkor a trystrtoint(szöveg,változó)
függvényt, mely a szöveget egésszé konvertálja és a megadott integer típusú változóba helyezi (ld. cím szerinti paraméterátadás), kimeneti értékként pedig visszaadja, sikeres volt-e a konverzió (vagyis a függvény típusa boolean). A példaprogram a beadott számokat összegzi, *-ra kilép. Ezért a beolvasás csak string típusú változóba mehet. A trystrtoint
szerepe a példában kettős: egyrészt megadja, sikeres-e a konverzió, másrészt (mellékhatásként) el is végzi a konverziót. Mellékhatással rendelkező függvények használata a programkódot nehezebben érthetővé, ugyanakkor tömörebbé teszi.
USES Sysutils; VAR s:string; n,x:integer; BEGIN x:=0; repeat write('Szám (*=kilépés): '); readln(s); if trystrtoint(s,n) then x:=x+n; until s='*'; writeln(x); readln; END.
A Free Pascal unitjaiban rengeteg hasznos segédeszközt találhatunk. Ezeket egy adott probléma megoldása közben érdemes megkeresni, akár keresővel (pl. "free pascal convert date to string" keresőkifejezés), akár a dokumentációt böngészve.
Feladatok
46. Egy szöveges fájlban (feladat4.txt) nevek szerepelnek keresztnév szóköz vezetéknév formában soronként. A program írja ki képernyőre a neveket vezetéknév vessző keresztnév formátumban!
47. Egy szöveges fájlban (feladat5.txt) állatnevek és az állatok maximális sebessége szerepel állatnév kettőspont szóköz sebesség formában, a sebesség tizedesponttal van megadva m/s-ban. A program írja ki az állatokat és a hozzájuk tartozó sebességet km/h-ban!