C-ben nagyon egyszerű a szövegfájlok kezelése. A szabványos bemenetet és kimenetet kezelő scanf()
és
printf()
függvényeknek is van olyan változata, amelyik fájlból és fájlba dolgozik. Ezek az fscanf()
és az fprintf()
. Ezek első paraméterként megkapják a fájlt, amellyel dolgozniuk kell, amúgy pedig a használatuk
teljesen megegyezik az előbb említett függvényekkel.
Fájlt megnyitni, létrehozni az fopen()
nevű függvénnyel lehet. Ennek visszatérési értéke egy ún.
file handle, amellyel a megnyitott fájlra hivatkozunk (mert egyszerre több fájllal is dolgozhatunk). A használat
nagyon röviden az alábbi programban látszik. Ez a klasszikus „helló, világ” program, azzal a különbséggel, hogy nem
a képernyőre, hanem a hello_world.txt
fájlba írja az üzenetet.
#include <stdio.h>
int main(void) {
/* Az fp változóval hivatkozunk majd a nyitott fájlra. */
FILE* fp;
/* Létrehozzuk a fájlt, w = write = írni fogunk bele. */
fp = fopen("hello_world.txt", "w");
/* Beleírjuk a "Helló, világ!" szöveget. */
fprintf(fp, "Helló, világ!\n");
/* Végeztünk, bezárjuk a fájlt. */
fclose(fp);
return 0;
}
A megnyitás sikerességét egyébként ellenőrizni kell, mert előfordulhat, hogy nem sikerül valamilyen okból
létrehozni a fájlt (pl. rossz helyen próbáljuk, nincs oda írási jogunk, és így tovább). A hibát úgy látjuk, hogy
az fopen()
függvény NULL
értéket ad vissza. Ilyenkor a perror()
-ral
szokás hibaüzenetet kiírni, mert az egyből kiírja a sikertelenség okát is. És természetesen ilyenkor a
fájlműveleteket (írás, zárás) nem végezhetjük el, mert nincs értelme.
#include <stdio.h>
int main(void) {
FILE* fp;
fp = fopen("szamok.txt", "w"); /* file-open, w = write */
if (fp != NULL) {
for (int i = 1; i <= 10; ++i)
fprintf(fp, "%d\n", i); /* file-printf */
fclose(fp); /* file-close */
}
else {
perror("Nem sikerült megnyitni a fájlt");
}
return 0;
}
Az olvasás ugyanez; w
helyett r
(mert read), és fprintf()
helyett fscanf()
.
Beolvasás közben a fájlból sorban kapjuk az adatokat, az elejétől végéig; mintha a fájl tartalmát a felhasználó folyamatosan
gépelné be.
Fájl beolvasásánál gyakori az, hogy nem közvetlenül a fájlból fscanf()
-elünk, hanem komplett sorokat olvasunk be,
és utána a beolvasott sorokból, sztringekből vesszük ki az adatokat. Ez azért előnyös, mert így könnyebb kezelni a hibás fájlokat:
tudjuk, hogy mekkora egységeket olvasunk be a fájlból, nem a sor közepén akad el hiba esetén a beolvasás. A beolvasott sor tartalma
alapján pedig bonyolultabb esetszétválasztásokat is meg tudunk csinálni. A módszer ehhez hasonló:
FILE* fp;
fp = fopen("fajl.txt", "r"); /* r = read */
/* ... */
char sor[101];
fgets(sor, 101, fp);
/* beolvasott sor kezelése... */
if (baj_van) {
printf("Hibás sor: %s", sor);
}
/* ... */
A beolvasott soron akár sscanf()
, strtok()
vagy más sztringkezelő függvények is használhatók.
A mappák kezelésére elvileg nincsen szabványosított módszer. Azonban a gyakorlatban szinte minden környezetben rendelkezésre
áll a dirent.h
nevű fejlécfájl, és annak opendir()
, readdir()
és closedir()
nevű függvényei.
Ha egy mappa tartalmát listázni szeretnénk, előbb meg kell nyitni azt az opendir()
nevű függvénnyel. Ez egy
DIR *
típusú mutatót ad vissza; az fopen()
-hez hasonlóan NULL
értékkel jelzi, ha hiba volt.
Ha nem, akkor a readdir()
függvény sorozatos hívásaival kapjuk meg az egyes bejegyzések adatait. Ha elfogytak, akkor
ez is NULL
értéket ad. Végül pedig bezárhatjuk a mappát a closedir()
függvénnyel.
A readdir()
függvény egy struct dirent *
típusú mutatót ad arra a struktúrára, amelyből a fájl adatai
kiolvashatóak. Ennek tartalma operációs rendszertől függő, de ami biztosan van, az a d_name
nevű adattag: ebben van
a fájl (vagy épp mappa) neve.
A három függvény használatát az alábbi kód mutatja be:
#include <dirent.h>
#include <stdio.h>
int main(void) {
DIR *d = opendir("."); // melyik mappát
if (d == NULL) {
perror("Nem sikerült megnyitni");
} else {
struct dirent *de;
while ((de = readdir(d)) != NULL) {
printf("%s\n", de->d_name);
}
closedir(d);
}
return 0;
}
Ügyelni kell arra, hogy a listában benne lehet az aktuális mappát jelképező .
, és a szülő mappát jelképező
..
is. Ha ezekre nincsen szükség, akkor ki kell őket szűrni a listázás közben.
Az egyes listázott fájlok többféle típusúak lehetnek, pl. rendes fájlok, vagy további mappák is. Egy fájl tulajdonságai, elérési
jogai és egyéb információk a stat()
nevű függvénnyel kérdezhetőek le, amelyik egy struct stat
típusú
változóba írja be az adatokat. Legfontosabb ezek közül az st_mode
, a fájl típusa, és az st_size
, a fájl
mérete. Az st_mode
adattag tartalmát az S_ISREG()
és S_ISDIR()
makrók segítenek értelmezni:
rendes fájl-e, mappa-e. Az st_size
pedig csak fájlnál hasznos információ, mappánál nem igazán.
Példa a stat()
használatára:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(void) {
struct stat s;
if (stat("pelda.txt", &s) < 0) { // melyik fájlt/mappát
perror("Hiba, stat() sikertelen");
} else {
if (S_ISREG(s.st_mode))
printf("Ez egy fajl, merete: %ld bajt", s.st_size);
else if (S_ISDIR(s.st_mode))
printf("Ez egy mappa");
else
printf("Egyeb");
}
return 0;
}