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.

Előző     Tartalom     Következő

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!

megoldás

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!

megoldás