1. Sissejuhatus
Programmeerimises on failide lugemine ja kirjutamine üks väga olulisi toiminguid. C-keeles on oluline mõista faili avamise, andmete kirjutamise ja faili sulgemise põhiprintsiipe. Selles artiklis selgitame põhjalikult C-keeles failikirjutamise põhilisi meetodeid ja toome konkreetseid näiteid.
Faili kirjutamist kasutatakse andmete püsivaks salvestamiseks ja andmete jagamiseks teiste programmidega, mistõttu on see oskus väga oluline paljudes programmides. Failikäitlust C-keeles õppides muutub failikäitlus ka teistes programmeerimiskeeltes kergemini arusaadavaks. Selle artikli abil õpid nii põhilisi kirjutamisviise kui ka edasijõudnud vigade käsitlemist ning süvendad oma arusaamist failikäitlusest.
Järgmises peatükis selgitame faili avamise ja sulgemise toiminguid ning kirjutamisrežiime alates põhitasemest.
2. Faili kirjutamise põhitõed
Faili kirjutamiseks C-keeles tuleb esmalt fail avada. Faili avamisel on vaja määrata „mis eesmärgil” fail avatakse. C-keeles kasutatakse faili avamiseks funktsiooni fopen
ja sulgemiseks funktsiooni fclose
. Siin selgitame faili avamise ja sulgemise põhitööd ning kirjutamisrežiime.
fopen
funktsiooni kasutamine
Faili avamiseks kasutatakse funktsiooni fopen
, mis võtab argumentideks failinime ja režiimi (failikäitluse tüüp). fopen
-i põhivorm on järgmine:
FILE *fopen(const char *filename, const char *mode);
filename
: avatava faili nimi (tee)mode
: faili avamise viis (kirjutamine, lugemine, lisamine jms)
Kirjutamisrežiimide tüübid
Faili avamiseks on mitu režiimi. Siin on toodud peamised kirjutamisega seotud režiimid:
"w"
: Ainult kirjutamiseks. Kui fail juba eksisteerib, kustutatakse selle sisu. Kui fail puudub, luuakse uus."a"
: Ainult lisamiseks. Kui fail eksisteerib, lisatakse andmed lõppu. Kui fail puudub, luuakse uus."wb"
: Binaarse kirjutamise režiim. Kui fail eksisteerib, kustutatakse sisu ja kirjutatakse binaarvormingus. Kui fail puudub, luuakse uus.
Näide kirjutamisest
Allpool on toodud näide, kuidas luua uus fail ja sinna kirjutada. Kui fail juba eksisteerib, kustutatakse selle sisu ja kirjutatakse „w”-režiimis.
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w"); // Ava fail "w"-režiimis
if (file == NULL) {
printf("Faili ei saanud avada.\n");
return 1;
}
fprintf(file, "Tere, see on failikirjutamine C-keeles!\n"); // Kirjuta faili
fclose(file); // Sulge fail
printf("Faili kirjutamine on lõpetatud.\n");
return 0;
}
Selles näites luuakse funktsiooniga fopen
fail „example.txt” ja kirjutatakse fprintf
-iga tekstiandmed. Pärast kirjutamist tuleb alati sulgeda fail fclose
-iga, vastasel juhul võib juhtuda, et andmeid ei salvestata õigesti.
fclose
funktsiooni olulisus
fclose
on funktsioon, mida tuleb alati kutsuda pärast faili avamist. Faili sulgemine vabastab süsteemi ressursid ja tagab andmete salvestamise. Kui faili ei suleta ja programm lõpeb, võib kirjutamine katkeda. Harjuta alati faili sulgema pärast kasutamist.
Järgmises peatükis vaatleme üksikasjalikumalt, kuidas kirjutada teksti faili.
3. Tekstifaili kirjutamise meetodid
C-keeles saab teksti faili kirjutada kolmel viisil: märkhaaval, stringhaaval ja vormindatud andmete kaupa. Iga meetodi jaoks on olemas vastav funktsioon, mida saab kasutada vastavalt vajadusele. Selles jaotises selgitame funktsioonide fputc
, fputs
ja fprintf
kasutamist.
fputc
– ühe märgi kirjutamine
fputc
kirjutab faili ühe märgi korraga. See on lihtne meetod, mida kasutatakse peamiselt siis, kui on vaja tegeleda üksikute märkidega. Süntaks on järgmine:
int fputc(int character, FILE *stream);
character
: kirjutatav märkstream
: failipointer
fputc
kasutamise näide
Allpool on näide, kus faili kirjutatakse üksikuid märke:
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w");
if (file == NULL) {
printf("Faili ei saanud avada.\n");
return 1;
}
fputc('A', file);
fputc('B', file);
fputc('\n', file);
fclose(file);
printf("Märkide kirjutamine on lõpetatud.\n");
return 0;
}
Selles näites kirjutatakse faili tähed 'A'
ja 'B'
ükshaaval. Sobib väikeste andmete salvestamiseks.
fputs
– stringi kirjutamine
fputs
kirjutab faili terve stringi korraga. See on tõhus viis teksti salvestamiseks, kui ei ole vaja töötada märkhaaval. Süntaks:
int fputs(const char *str, FILE *stream);
str
: kirjutatav stringstream
: failipointer
fputs
kasutamise näide
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w");
if (file == NULL) {
printf("Faili ei saanud avada.\n");
return 1;
}
fputs("See on näide fputs-funktsiooni kasutamisest.\n", file);
fclose(file);
printf("Stringi kirjutamine on lõpetatud.\n");
return 0;
}
See meetod sobib, kui soovitakse kiiresti ja tõhusalt stringi faili salvestada.
fprintf
– vormindatud andmete kirjutamine
fprintf
on printf
-i failiversioon, mis võimaldab kirjutada vormindatud andmeid. See sobib, kui on vaja salvestada nii numbreid kui ka teksti kindlas vormingus.
int fprintf(FILE *stream, const char *format, ...);
stream
: failipointerformat
: vormingustring koos formaadimäärangutega
fprintf
kasutamise näide
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w");
if (file == NULL) {
printf("Faili ei saanud avada.\n");
return 1;
}
int number = 123;
float decimal = 45.67;
fprintf(file, "Täisarv: %d, Komaarv: %.2f\n", number, decimal);
fclose(file);
printf("Vormindatud andmete kirjutamine on lõpetatud.\n");
return 0;
}
Siin salvestatakse nii täisarv kui ka komaarv etteantud vormingus.
Kokkuvõte
fputc
, fputs
ja fprintf
on peamised funktsioonid tekstifaili kirjutamiseks C-keeles. Õige meetodi valimine sõltub andmete tüübist ja salvestusviisist.
4. Binaarfaili kirjutamise meetod
C-keeles saab lisaks tekstile salvestada ka binaarandmeid, näiteks pilte, helifaile või struktuure. Selleks kasutatakse fwrite
-funktsiooni.
fwrite
kasutamine
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
ptr
: viit kirjutatavale andmelesize
: ühe elemendi suurus baitidescount
: kirjutatavate elementide arvstream
: failipointer
Binaarrežiimis avamine
Binaarfaili avamiseks kasuta režiime nagu "wb"
(kirjutamine) või "ab"
(lisamine), et andmed salvestataks muutmata kujul.
fwrite
näide
#include <stdio.h>
int main() {
FILE *file = fopen("example.bin", "wb");
if (file == NULL) {
printf("Faili ei saanud avada.\n");
return 1;
}
int data[] = {10, 20, 30, 40, 50};
size_t dataSize = sizeof(data) / sizeof(data[0]);
fwrite(data, sizeof(int), dataSize, file);
fclose(file);
printf("Binaarandmete kirjutamine on lõpetatud.\n");
return 0;
}
Olulised tähelepanekud
- Andmete ühilduvus: binaarandmete lugemine teises süsteemis võib põhjustada probleeme, kui arhitektuur erineb.
- Endianness: erinevates süsteemides võib baitide järjestus olla erinev. Vajadusel tee teisendused.
- Reavahetused: binaarrežiimis ei muudeta reavahetusi automaatselt.
Kokkuvõte
Binaarsete andmete kirjutamine on efektiivne viis salvestada keerulisi andmestruktuure. fwrite
võimaldab seda teha kiiresti ja paindlikult.
5. Vigade käsitlemine
Failidega töötades võivad tekkida vead, näiteks kui fail puudub või puuduvad juurdepääsuõigused. Vigade korrektne käsitlemine aitab vältida ootamatut programmi käitumist ja parandab töökindlust. Selles jaotises selgitame C-keele failikäitluse veahaldust.
Faili avamise vea kontroll
Kui faili avamine fopen
-iga ebaõnnestub, tagastab see NULL
. Seda saab kasutada vea tuvastamiseks ja sobiva teate kuvamiseks.
Näide faili avamise vea kontrollist
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
perror("Faili ei saanud avada");
return 1;
}
// Failiga töötlemine
fclose(file);
return 0;
}
perror
kuvab automaatselt süsteemipõhise vea kirjelduse, mis aitab põhjust tuvastada.
perror
ja strerror
kasutamine
perror
: kuvab teate koos süsteemi veakirjeldusega standardväljundisstderr
.strerror
: tagastab veakoodi tekstikirjelduse.
strerror
näide
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
printf("Viga: %s\n", strerror(errno));
return 1;
}
fclose(file);
return 0;
}
Siin kasutatakse errno
ja strerror
, et saada ja kuvada vea kirjeldus.
Kirjutamisvigade tuvastamine ja käsitlemine
Faili kirjutamise ajal võib tekkida vigu, näiteks kettaruumipuuduse tõttu. ferror
abil saab kontrollida, kas kirjutamisel tekkis viga.
Näide kirjutamisvea tuvastamisest
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w");
if (file == NULL) {
perror("Faili ei saanud avada");
return 1;
}
if (fprintf(file, "Andmete kirjutamine") < 0) {
perror("Kirjutamisviga");
fclose(file);
return 1;
}
fclose(file);
printf("Kirjutamine lõpetatud.\n");
return 0;
}
Kui fprintf
tagastab negatiivse väärtuse, käsitletakse seda veana ja väljastatakse teade.
Kokkuvõte
Veakäsitlus on oluline töökindlate programmide loomisel. Faili avamise või kirjutamise ajal ilmnenud probleemidele tuleb reageerida asjakohaste teadetega.
6. Rakendusnäited
Kui oled failikirjutamise põhialused omandanud, saad seda kasutada erinevates praktilistes olukordades, näiteks logifailide pidamisel, seadistusfailide loomisel ja andmete serialiseerimisel.
Logifaili kirjutamine
Logifailide abil saab jälgida programmi tööd ja vigu.
Näide logifaili kirjutamisest
#include <stdio.h>
#include <time.h>
void log_message(const char *message) {
FILE *file = fopen("log.txt", "a");
if (file == NULL) {
perror("Logifaili ei saanud avada");
return;
}
time_t now = time(NULL);
struct tm *t = localtime(&now);
fprintf(file, "[%04d-%02d-%02d %02d:%02d:%02d] %s\n",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec, message);
fclose(file);
}
int main() {
log_message("Programm käivitati.");
log_message("Tekkis viga.");
return 0;
}
Seadistusfaili loomine
Seadistusfailid salvestavad rakenduse seaded ja võimaldavad neid programmi käivitamisel laadida.
Näide seadistusfaili loomisest
#include <stdio.h>
void save_settings(const char *filename, int volume, int brightness) {
FILE *file = fopen(filename, "w");
if (file == NULL) {
perror("Seadistusfaili ei saanud avada");
return;
}
fprintf(file, "volume=%d\n", volume);
fprintf(file, "brightness=%d\n", brightness);
fclose(file);
}
int main() {
save_settings("settings.conf", 75, 50);
printf("Seadistusfail salvestatud.\n");
return 0;
}
Andmete serialiseerimine ja deserialiseerimine
Serialiseerimine tähendab andmestruktuuri salvestamist faili kujul, et seda hiljem taastada.
Näide struktuuri salvestamisest ja laadimisest
#include <stdio.h>
typedef struct {
int id;
char name[50];
float score;
} Student;
void save_student(const char *filename, Student *student) {
FILE *file = fopen(filename, "wb");
if (file == NULL) {
perror("Faili ei saanud avada");
return;
}
fwrite(student, sizeof(Student), 1, file);
fclose(file);
}
void load_student(const char *filename, Student *student) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
perror("Faili ei saanud avada");
return;
}
fread(student, sizeof(Student), 1, file);
fclose(file);
}
int main() {
Student s1 = {1, "Taro Sato", 89.5};
save_student("student.dat", &s1);
Student s2;
load_student("student.dat", &s2);
printf("ID: %d, Nimi: %s, Tulemus: %.2f\n", s2.id, s2.name, s2.score);
return 0;
}
Kokkuvõte
Logifailid, seadistusfailid ja serialiseerimine on tavalised failikirjutuse rakendused. Nende oskamine muudab programmeerija töö palju paindlikumaks.
7. Korduma kippuvad küsimused (KKK)
Faili ei saa avada
K: fopen
tagastab NULL
. Mida teha?
V: Kontrolli faili teed, õigusi, kettaruumi ja kasuta perror
või strerror
vea täpsustamiseks.
Kirjutamine ei kajastu failis
K: Faili kirjutatud andmed ei ilmu koheselt.
V: Sulge fail fclose
-ga või kasuta fflush
, et puhverdus koheselt tühjendada.
Teksti- ja binaarfailide erinevus
K: Mis vahe on?
V: Tekstifail salvestab märgid ja teeb võimalikud formaadimuutused (nt reavahetused), binaarfail salvestab andmed muutmata kujul.
Endianness probleemid
K: Erinevad süsteemid tõlgendavad binaarandmeid valesti.
V: Tee baitide järjekorra teisendus (htonl
, htons
) või lisa failiformaati endianness info.
8. Kokkuvõte
Selles artiklis käsitlesime C-keele failikirjutamist algtasemest kuni rakendusnäideteni. Õppisime failide avamist ja sulgemist (fopen
, fclose
), teksti- ja binaarfailide kirjutamist (fputc
, fputs
, fprintf
, fwrite
), vigade käsitlemist (perror
, strerror
) ning praktilisi kasutusviise (logid, seaded, serialiseerimine). Failitöötlus on oskus, mis on vajalik pea igas programmeerimisprojektis ja mille tundmine aitab kirjutada töökindlat koodi.