7. gyakorlat Fájlkezelés IO haladó Függvények haladó
Fájlkezelés • A C-ben a fájlkezelés ún. fájlstream-eken keresztül történik, melyek puffereltek ha valamit a streamre küldünk, nem biztos, hogy rögtön kiíródik a fájlba. • A 3 eddig tanult iostream ▫ stdin - standard bemenet (billentyűzet) ▫ stdout - standard kimenet (konzol) ▫ stderr - standard hibakimenet (konzol)
• Ha mást szeretnénk használni új parancsokra lesz szükségünk.
Fájlkezelés • A fájlstreamek megnyitásához létre kell hozni egy FILE* típusú változót, majd az fopen függvénnyel meg kell nyitnunk a fájlt. • Példa: FILE* fajlom; fajlom = fopen("név", "mód"); • A név a fájl elérési útja, (abszolút, vagy relatív, relatív esetén a kezdőpont (az aktuális mappa) a program futtatható állományának mappája) a mód pedig a fájl megnyitásának módját jelzi.
• Használat után a streamet MINDEN ESETBEN le kell zárni az fclose(stream); paranccsal. (Pl. fclose(fajlom);) • A puffer ürítésére használható az fflush(stream); parancs, ami azonnal kiír mindent a fájlba, amit még nem írt ki a pufferből. • Ez stream lezárásakor automatikusan megtörténik, kézzel csak pl. olvasás és írás közötti váltáskor kell meghívni.
fopen paraméterezése A mód 1-3 karakteres sztring, formája: • 1. karakter: ▫ ▫ ▫
r, ha olvasásra nyitjuk meg fájlnak léteznie kell a parancs kiadásakor w ha írásraa fájlt létrehozza, ha nem létezik, ha már létezett, korábbi tartalma törlődik a, ha hozzáfűzésre a fájlt létrehozza, ha nem létezik. Ha már létezett, az új adatok a fájl végére kerülnek
• 2. karakter: +. Ha szerepel, a stream a másik műveletet is támogatni fogja ▫ ▫ ▫
r+ írható is lesz (de csak létező fájl nyitható meg és megnyitáskor a tartalma megmarad) w+ olvasható is lesz (de megnyitáskor a korábbi tartalma törlődik) a+ szintén támogatni fogja az olvasást, de az olvasás a fájl korábbi részén, az írás pedig az az utáni részen történik.
• 3. karakter: ▫ ▫
b, ha a fájl bináris fájl (pl. egy program, ebben bármilyen karakterek bárhol szerepelhetnek), t, ha szöveges fájl (speciális sorvégjelekkel, és fájl vége jellel). Ha elhagyjuk, szövegesnek tekinti a fájlt, mintha t-t írtunk volna.
• A 2. és 3. karakter felcserélhető, tehát pl. r+b és wt+ is érvényes módok.
F: Írj egy programot, ami beolvas két egész számot, majd kiírja az összegüket és a szorzatukat.
#include <stdio.h> int main() { int a, b; scanf("%d %d", &a, &b); printf("Osszeg: %d\nSzorzat: %d\n", a+b, a*b); return 0; }
F: Módosítsuk úgy a programot, hogy használja az stdin, stdout, fscanf és fprintf függvényeket.
#include <stdio.h> int main() { int a, b; fscanf(stdin, "%d %d", &a, &b); fprintf(stdout, "Osszeg: %d\nSzorzat: %d\n"); return 0; }
F: Módosítsuk úgy a programot, hogy valódi fájlokat használjon. #include <stdio.h> int main() { int a, b; FILE *infile; FILE *outfile; infile = fopen("be.txt", "r"); outfile = fopen("ki.txt", "w");
fscanf(infile, "%d %d", &a, &b); fprintf(outfile, "Osszeg: %d\nSzorzat: %d\n", a + b, a * b);
}
fclose(infile); fclose(outfile); return 0;
F: Hibakóddal lépjen ki a program, ha valamelyik fájl megnyitása nem sikerült. #include <stdio.h> int main() { int a, b; FILE *infile; FILE *outfile; if(!(infile = fopen("be.txt", "r"))) { return 1; } if(!(outfile = fopen("ki.txt", "w"))) { fclose(infile); return 1; } fscanf(infile, "%d %d", &a, &b); fprintf(outfile, "Osszeg: %d\nSzorzat: %d\n", a + b, a * b); fclose(infile); fclose(outfile); return 0; }
Vége a fájlnak? • Ha szeretnénk megállapítani, hogy elértük-e már a fájl végét, erre két módszerünk is van:
▫ a feof(file) függvény igazat ad vissza, ha az átadott FILE* típusú változó által mutatott streamben elértük a fájl végét ▫ a legtöbb beolvasó függvény az EOF konstanssal tér vissza, ha semmit nem tudott beolvasni, mivel elérte a fájl végét.
• Módszer 1:
while (!feof(fajlom)) { fgetc (fajlom); //csinálunkvalamit }
• Módszer 2: do {
c = fgetc (fajlom); //csinálunkvalamit } while (c != EOF);
IO haladó • A printf és scanf függvények nevében az f formázott műveleteket jelöl: megadhatunk egy "formátumsztringet", ami a várt input/kívánt output szerkezetét megadja, illetve különböző argumentumokat rendelhetünk a "formátum tagekhez". A formátumtagek % karakterrel kezdődnek. Erről kimerítő leírás olvasható a cplusplus.com printf, illetve scanf oldalán.
F: Írj egy programot, ami beolvas egy előjeltelen short int értéket, és nyolcas számrendszerbe átváltva írja ki.
#include <stdio.h>
int main() { unsigned short int v; scanf("%hu", &v); printf("%ho\n", v); return 0; }
F: Írj egy programot, ami beolvas egy hexadecimális egész számot, majd 15 karakter szélességben kiírja a decimális értékét, mindenképpen előjellel és vezető nullákkal.
#include <stdio.h> int main() { unsigned int v; scanf("%x", &v); printf("%+015u\n", v); return 0; }
F: Olvass be egy double és egy egész értéket, majd a valós értéket írasd ki az egészben megadott pontossággal.
#include <stdio.h> int main() { double ertek; int pontossag; scanf("%lf %d", &ertek, &pontossag); printf("%1.*lf\n", pontossag, ertek); return 0; }
F: Olvass be egy csupa kisbetűből álló, legfeljebb 20 karakteres sztringet, majd írasd ki 10 karakteren jobbra igazítva az első legfeljebb 8 karakterét. A bemeneten a kisbetűket közvetlenül bármi követheti.
#include <stdio.h> int main() { char str[21]; scanf("%20[a-z]", str); printf("%10.8s\n", str); return 0; }
F: Egy sor kiíratási formátuma: "nev: %s; pont: %d;". Olvasd be a kiírt számot úgy, ha tudod, hogy a kiírt sztring nem tartalmazhat pontosvesszőt. Ellenőrízd le, hogy az input sor valóban helyes-e. #include <stdio.h> int main() { int val, ret; ret=scanf("nev: %*[^;]; pont: %d;", &val);
}
if(ret==1) { printf("A szam: %d\n", val); } else { printf("Helytelen formatum\n"); } return 0;
Egyéb függvények • getchar: egy karakter beolvasása char c = getchar(); • putchar: egy karakter kiírása putchar('A'); • gets: egy sztring beolvasása char str[20]; gets(str); Egy egész sort beolvas, a végén az új sor karaktert NEM menti bele a stringbe (de a pufferből kiveszi). Vigyázni kell, hogy az átadott str változó elég nagy legyen. A C99 szabványtól kezdve használata nem ajánlott, a C11-ből pedig hiányzik, helyette a biztonságosabb gets_s használható benne. • puts: egy sztring kiírása puts("Ez egy szöveg"); Egy egész sort kiír, tehát végül kiír egy további újsor karaktert is.
F: Írasd ki a fájlvége jelig (^D) tartó bemenetet úgy, hogy a számjegyeket törlöd belőle. A végén írd ki, hogy hány számjegyet töröltél. #include <stdio.h> int main() { int c, d=0; while((c=getchar())!=EOF) { if('0'<=c && c<='9') { d++; } else { putchar(c); } } printf("\n--\n%d torolve\n", d); return 0; }
Rekurzív függvények • Egy függvény önmagát is meghívhatja, ezt rekurziónak, az ilyen függvényt rekurzív függvénynek nevezzük. Ez olyan esetekben hasznos, ha egy probléma vele azonos, de a megoldáshoz közelebb álló problémára vezethető vissza, mígnem egy olyan problémát kapunk, ami már triviálisan megoldható. • Rekurzív függvényeknél NAGYON FONTOS, hogy minden esetben legyen kilépési feltétel (ami előbb utóbb teljesül is), tehát egy olyan eset, amikor nem történik több rekurzív hívás.
F: Fibonacci-sorozat n. elemének kiszámítása rekurzív módszerrel #incude <stdio.h>
int fib(int n) { if(n==1 || n==2) { return 1; } else { return fib(n-1) + fib(n-2); } } int main() { int n; printf("n erteke?:\t"); scanf("%d", &n); printf("A Fibonacci-sorozat %d. eleme:\t%d", fib(n)); return 0; }
Feladatok • - Add meg N-dimenziós vektorok sorozatának összegét. A program először bekéri a vektorok méretét (max 100), majd a vektorokat. A vektorok a végpont valós koordinátáival adottak. A sorozat végét a nulla vektor jelzi. A program fájlból olvassa az adatokat és az eredményt is fájlba írja! A bemeneti fájl első sorában 2 szám van szóközzel elválasztva: az első a vektor mérete (N), a második a vektorok száma (M). Ezután M sor található, mindegyikben N db számmal, ezek az egyes vektorok koordinátái. (Egy sor egy vektor). Ezesetben végjelre (0 vektorra a sorozat végén) nincs szükség. A kimeneti fájlban egy sor legyen, benne (szóközökkel elválasztva) az összegvektor koordinátái. Hogyan lehetne azt megoldani, hogy a vektorok számát ne kelljen megadni, és végjel se legyen?
Feladatok • Írd meg az alábbi függvényeket úgy, hogy a megvalósításuk a main függvény mögött legyen. Írj egy főprogramot, amelyik mindegyik függvényt legalább egyszer meghívja. ▫ ▫ ▫ ▫ ▫ ▫ ▫ ▫ ▫ ▫
fact : kiszámolja egy szám faktoriálisát rec : kiszámolja egy szám reciprokát sqr : kiszámolja egy szám négyzetét cube : kiszámolja egy szám köbét add : kiszámolja két szám összegét sub : kiszámolja két szám különbségét mul : kiszámolja két szám szorzatát per : kiszámolja két szám hányadosát div : kiszámolja két szám egészosztásának hányadosát mod : kiszámolja két szám egészosztásának maradékát
Puzzle #include <stdio.h> void e(int); int main() { int a = 3; e(a); putchar('\n'); return 0; } void e(int n) { if (n > 0) { e(--n); printf("%d ", n); e(--n); } }
• Melyik a program outputja? Miért? a) b) c) d)
0120 0121 1201 0211
Puzzle2 int foo(int x, int n) { int val = 1; if (n > 0) { if (n % 2 == 1) val *= x; val *= foo(x * x, n / 2); } return val; }
Mit számol ki a foo függvény? Miért? a) b) c) d)
x^n x*n n^x Egyik sem