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.
 
 

Kimmel Tamás
eMail:PC-XUser@IDG.HU, Subject: "abC rovat"
KUMM@MI.STUD.PMMFK.JPTE.HU