Mivel már épp elég elméleti számot csináltam, úgy gondolom nézzünk valami használható dolgot. Egy kis telefonkönyvprogramot kezdek el írni, amit persze könnyen át lehet alakítani bármilyen nyilvántartóvá. Ebben a számban még elég primitív formában, de már működni fog. Tehát most a file kezelés, és az adatok kezelése lesz a lényeg, a külalak még lényegtelen. Aztán a következő számokban rátérünk a menüvezérlésre, és a memóriakezelésre. Induljunk el a kályhától!
Mit kell tudnia egy ilyen programnak. El kell tudni tárolni a telefonszámokat egy file-ban, és innen bármikor visszakeresni, módosítani. A file kezelés legegyszerűbb módjáról, a handle-el való kezelésről már volt szó, most egy profibb megoldást fogunk használni, ami a handle-es megoldással szemben operációs rendszertől független. Úgynevezett stream-eket kezelünk. A stream tulajdonképpen egy képzelt file, valójában takarhat perifériákat is. Két stream-et már ismerünk, ezek a standard output(stdout), és a standard input (stdin). Tehát hozzárendelünk a file-hoz egy stream-et, és ezt írjuk. Nem is biztos, hogy amit írtunk az rögtön meg fog jelenni a file-ban, ugyanis a stream-ek buffereltek. A stream-ekkel csak elméletileg írjuk a vele összerendelt file-t.
A gyakorlatban:
A file-ra nem egy mutatóval hivatkozunk, hanem egy FILE típusú pointerrel.
Ez a kezelési módszer elvontabb, jobban elrejti előlünk a valódi történéseket.
Nézzünk, hogy kell egy file-t stream-ként megnyitni:
FILE *datfile; //a filera a datfile nevű változóval fogunk hivatkozni
datfile=fopen(filename,"r+"); //megnyitás
Azt hiszem, csak az az "r+" szorul magyarázatra. Ezzel adjuk meg a megnyitási módot.
Ezek közül:
r Csak olvasásra nyit meg már létező file-t.
w Csak írásra hozza létre a file-t. Ha a file már létezik, felülírja.
a Csak file végi írásra nyitja meg a file-t, tehát ha írunk valamit
a file-ba, az a végéhez fog láncolódni. Ha a file nem létezik, létre is
hozza.
r+ Már létező file-t nyit meg írásra és olvasásra.
w+ Létrehozza a file-t írásra és olvasásra. Ha a file már létezik,
felülírja.
a+ Olvasásra és file végi írásra nyitja meg a file-t, tehát
ha írunk valamit a file-ba, az a végéhez fog láncolódni. Ha a file nem
létezik, létre is hozza.
Eyen túl még azt is meg tudjuk adni, hogy szövegesen, vagy binárisan
kívánjuk kezelni a file-t. Ha egy "t" betűt írunk a mód után, akkor a file
szöveges lesz, ha "b" betűt, akkor bináris.
pl.: w+t Létrehozza a file-t írásra és olvasásra. Ha a file már létezik,
felülírja.
És a file-t szövegesen kezeli.
Ha nem adjuk meg, hogy szöveges, vagy bináris legyen, akkor az _fmode
vátozó fogja megadni.
A függvénynek van visszatérési értéke is. Ha 1, akkor a megnyitás sikeres
volt, ha 0, akor sikertlen.
Tehát, ha akarunk írni egy olyan függvényt, ami írásra és olvasásra
megnyit egy file-t, ha nem létezik, akkor meg létrehozza, akkor valahogy
így fog kinézni:
int openfile(char *filename)
{
if ((datfile=fopen(filename,"r+"))==NULL)
{
printf("Az adatfile
nem letzik, letrehozok egy masikat...");
if ((datfile=fopen(filename,"w"))==NULL)
{
printf("Nem
tudom letrehozni az adatfile-t");
return(0);
}
fclose(datfile);
datfile=fopen(filename,"r+");
}
return(1);
}
Adatfile-unk már biztosan van. Most már csak kezelni kéne. Ehez nem kell más, csak, hogy írni, olvasni, meg pozícionálni tudjunk benne.
Olvasás a file-ból:
fread(&rek,sizeof(ADAT),1,datfile);
Az első paraméter az a memóriacím, ahova olvasunk, a második paraméter az olvasandó rekordok hossza, a harmadik az olvasandó rekordok száma, a negyedik az olvasandó file.
Írás a file-ba:
fwrite(&rek, sizeof(ADAT), 1, datfile);
Az első paraméter az a memóriacím, ahonnan írunk, a második paraméter az írandó rekordok hossza, a harmadik az írandó rekordok száma, a negyedik az írandó file.
Pozícionálás:
fseek(datfile,0,SEEK_END);
Az első paraméter a file, a második a pozíció, a harmadik, hogy mihez képest pozícionálunk. Ez lehet:
SEEK_END file végéhez képest. (a példa a file végére pozícionál)
SEEK_SET file elejéhez képest
SEEK_CUR aktuális pozícióhoz képest
Most már elvileg bármit meg tudunk csinálni az adatfile-unkal. Csak meg kell írni. De van még egy rázós kérdés. Mint említettem, a stream-ek buffereltek. Tehát, ha írunk egyfile-ba, akkor a file valójában nem biytos, hogy rögtön íródik, így a visszaolvasásnál még a régi eredménzeket kapjuk. Valahogy rá kell venni a stream-et, hogy szabaduljon meg a buffer tartalmától. Erre szolgál:
fflush(stdin);
Ebben az esetben a standard input bufferét ürítettük.
Akkor kezdhetjük az érdemi munkát. Először meg kell adnunk a tárolandó
rekordok típusát:
typedef struct {
char nev[30];
char szam[15];
char cim[30];
char reserved;
} ADAT;
A típus neve ADAT lesz, 4 mezőből áll. Név, cím, és telefonszám. A negyedik egyelőre még nem használt, de majd. Azért került be már most is, hogy az ezzel a programmal létrehozott adatfile a követekző számokhoz is jó legyen.
Aztán az adatfile-t:
FILE *datfile;
És jöhet a main():
void main(void)
{
char c;
void uj ();
void lista ();
long keres ();
void modosit ();
int openfile(char *filename);
clrscr();
if (openfile(FILENAME))
{
do
{
clrscr();
printf("Fomenu:\n");
printf(
"\n1 - uj
szam"
"\n2 - listazas"
"\n3 - kereses"
"\n4 - modositas"
"\n0 - kilepes");
switch(c=getch())
{
case '1' : uj(); break;
case '2' : lista(); break;
case '3' : keres(); break;
case '4' : modosit(); break;
case '0' : printf("\n\n C S A !"); break;
} // switch
fflush(datfile);
} while(c!='0');
fclose(datfile);
}
} // main
Amint látható a program 4 féle dologra van felkészülve. Bevitel, listázás, keresés, módosítás. Azt hiszem semmi új dolgot nem tartalmaz.
Nézzük a bevitelt:
void uj()
{
ADAT rek;
fseek(datfile,0,SEEK_END);
do
{
clrscr();
printf("Kerem az adatokat\n\n");
printf("Nev : ");
fflush(stdin);
gets(rek.nev);
printf("Telefonszam : ");
fflush(stdin);
gets(rek.szam);
printf("Lakcim : ");
fflush(stdin);
gets(rek.cim);
rek.reserved=0;
fwrite(&rek, sizeof(ADAT), 1, datfile);
printf("\n\nVan meg adat? (i/n) ");
} while(tolower(getch())!='n');
} // uj
Mivel természetesen a file végéhez akarunk bővíteni, először a végére kell pozícionálni. Az adatokat a lokális rek nevű változóba gyűjtjük be, ami természetesen ADAT típusú, és ezt írjuk a file végére. Egyszerű.
Listázás:
void lista()
{
ADAT rek;
int y=2;
clrscr();
printf(" nev telefonszam Lakcim\n");
fseek(datfile,0,SEEK_SET);
fread(&rek,sizeof(ADAT),1,datfile);
while(!feof(datfile))
{
gotoxy(1,y);
printf("%s",rek.nev);
gotoxy(40-strlen(rek.szam)/2,y);
printf("%s",rek.szam);
gotoxy(80-strlen(rek.cim),y);
printf("%s",rek.cim);
fread(&rek,sizeof(ADAT),1,datfile);
if (y==24)
{
bill();
clrscr();
printf("
nev telefonszam Lakcim/n");
y=2;
} else y++;
}
bill();
} // lista
Itt is lokális a rekord változónk. Először a file elejére kell pozícionálni, és innen szép sorjában olvasni az adatokat. Mivel a rekordunk ki fér egy sorban, az áttekinthetőség kedvéért így is írjuk ki. Minden képernyőnyi adat után várunk egy billenntyűt (bill()), és töröljük a képet.
Keresés:
long keres()
{
ADAT rek;
int nincs;
long i;
char string[30];
clrscr();
printf("Kerem a nevet! ");
fflush(stdin);
gets(string);
nincs=1;
clrscr();
fseek(datfile,0,SEEK_SET);
fread(&rek,sizeof(ADAT),1,datfile);
for(i=0;!feof(datfile) && nincs;i++)
{
if(strcmp(string,rek.nev)==0)
{
printf("Nev: %s\n",rek.nev);
printf("Telefonszam: %s\n",rek.szam);
printf("Lakcim: %s\n",rek.cim);
nincs=0;
} // if
fread(&rek,sizeof(ADAT),1,datfile);
} // for
if(nincs)
{
printf("\n Ilyen nevet nem talatam.");
bill();
return(-1);
}
bill();
return(i-1);
} // kereses
A függvény név szerint keres. A "string" változóba beolvassa a keresendő nevet, majd végigszalad az egész adatfile-on, addig, amíg nem talál olyan rekordot, ahol a név egyenlő a "string"-el. Ez a keresési forma elég lassú, de lesz ez még így se! A függvénynek van visszatérési értéke, mégpedig a talált rekord pozíciója a file-ban. Ha nem talál semmit, akkor -1-el tér vissza.
Módosítás:
void modosit()
{
ADAT rek;
long poz;
clrscr();
fflush(stdin);
if ((poz=keres())!=-1)
{
poz*=sizeof(ADAT);
printf("Kerem az uj nevet:\n");
fflush(stdin);
gets(rek.nev);
printf("Kerem az uj szamot:\n");
fflush(stdin);
gets(rek.szam);
printf("Kerem az uj cimet:\n");
fflush(stdin);
gets(rek.cim);
rek.reserved=0;
fseek(datfile, poz, SEEK_SET);
fwrite(&rek, sizeof(ADAT), 1, datfile);
printf("modositas megtortent");
bill();
}
} // modositas
A módosítás szintén úgy van megoldva, hogy a nevet kell megadni, és ehez a névhez tartozó rekordot módosítjuk. Először keresni kell. Tehát meghívjuk a kereső függvényt, és ennek a visszatérési értékét használjuk fel. (Ez a megoldás nem valami elegáms, de most ez volt a legegyszerűbb) Innen már könnyű dolgunk van, már tudjuk a rekord pozícióját. Oda pozícionálunk, és beírjuk a bekért javított adatokat.
Ennyi lenne.
A következő számban tovább pofásítjuk, 2-3 szám után már egész használható
lesz.