© Harry Broeders.
Deze pagina is bestemd voor studenten van de Haagse Hogeschool - Academie voor Technology, Innovation & Society Delft.
De programmeertaal C onderscheidt 2 soorten files :tekstfiles en binaire files. Deze webpagina behandeld aleen binaire files. Meer informatie over tekstfiles kun je hier vinden.
getc
of fscanf
. Schrijven in tekstfiles gaat met
de functie: putc
of fprintf
.
int
variabele). De inhoud van de file in hexadecimale notatie
is dan 00 00 00 7B. Binaire files worden ook wel random access files genoemd.
Als je ervoor zorgt dat de file ingedeeld is in blokken van gelijke grootte
dan kun je snel het blok met een specifieke index zoeken zonder dat je alle
voorgaande blokken moet inlezen. Lezen uit binaire files gaat met de functie:
fread
. Schrijven in binaire files gaat met de functie
fwrite
. Zoeken gaat met de functie fseek
.
Je kunt de volgende bewerkingen op binaire files uitvoeren:
Om een binairefile te kunnen lezen of beschrijven moet de file eerst geopend
worden. Dit gaat bijna hetzelfde als het
openen van een tekstfile alleen moet je bij de tweede parameter
mode
de letter b
toevoegen. Voorbeeld:
FILE *infile; infile = fopen("infile.bin", "rb");
Het is handig om een speciale extensie te gebruiken voor binaire files (bijvoorbeeld .bin). Bedenk echter dat het operating systeem zich niets aantrekt van deze extensies. Je kunt een binairefile dus rustig infile.txt noemen.
Het sluiten van een binairefile gaat exact hetzelfde als het sluiten van een tekstfile.
Een binairefile kan beschreven worden met de functie fwrite
.
Deze functie schrijft een aantal bytes in de file en is in de include file
stdio.h
als volgt gedeclareerd:
size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream);
Als eerste parameter moet een pointer worden meegegeven naar de variabele
(of variabelen) die moet(en) worden weggeschreven. Als tweede parameter moet
de grootte van één variabele (in bytes) worden opgegeven. Meestal
wordt de operator sizeof
gebruikt
om de grootte te bepalen. Als derde parameter moet het aantal variabelen
dat moet worden weggeschreven worden meegegeven. (Het is alleen zinvol om
een aantal > 1 op te geven als de pointer ptr
wijst naar
een array! Als vierde parameter moet een FILE*
worden meegegeven
die je hebt teruggekregen van de functie fopen
.
De functie fwrite
geeft het aantal variabelen terug dat succesvol
is weggeschreven.
In het voorbeeldprogramma write_123.c wordt aan de integer variabele
i
de waarde 123
toegekend en daarna wordt deze
variabele weggeschreven in een binairefile. (Dit is natuurlijk volkomen zinloos
maar maakt wel duidelijk hoe het werkt.)
#include <stdio.h>
int main(void) {
int i = 123;
FILE *fp;
size_t ret;
fp = fopen("123.bin", "wb");
if (fp == NULL) {
printf("Kan file 123.bin niet openen!");
}
else {
ret = fwrite(&i, sizeof i, 1, fp);
if (ret != 1) {
printf("Er is iets fout gegaan bij schrijven in file 123.bin!");
}
else {
printf("Er zijn %d variabelen geschreven in file 123.bin!", ret);
}
fclose(fp);
}
getchar();
return 0;
}
De uitvoer van dit programma is als volgt:
In het directory waar de executable file is uitgevoerd is nu de file 123.bin aangemaakt. In de filebrowser kunnen de "eigenschappen" van deze file bekeken worden. De file blijkt 4 bytes groot te zijn. Deze file kan wel met notepad worden geopend maar je ziet dan "vreemde" karakters:
Met de hexeditor kunnen we de inhoud van de file zowel in hexadecimale codering als in ASCII codering bekijken.
In het deel met de grijze achtergrond zien we de hex codes van de bytes uit de file. In het deel met de witte achtergrond zien we de bijbehorende ASCII karakters. Het is nu duidelijk te zien dat de file bestaat uit 4 bytes. Er is geen end-of-file karakter! De vier bytes bevatten tesamen het 32 bits hexadecimale getal 0000007B. Dit komt overeen met de decimale waarde 123. Je ziet dat het minst significante byte als eerste in de file wordt geschreven (deze volgorde wordt little-endian genoemd).
In het voorbeeldprogramma write_ints.c
zie je hoe met één
aanroep van write
een hele array met getallen kan worden
weggeschreven:
#include <stdio.h>
#define AANTAL 100
int main(void) {
int rij[AANTAL], i;
FILE *fp;
size_t ret;
for (i = 0; i < AANTAL; ++i) {
rij[i] = i * i * i;
}
fp = fopen("getallen.bin", "wb");
if (fp == NULL) {
printf("Kan file getallen.bin niet openen!");
}
else {
ret = fwrite(&rij[0], sizeof rij[0], AANTAL, fp);
if (ret != AANTAL) {
printf("Er is iets fout gegaan bij schrijven in file getallen.bin!");
}
else {
printf("Er zijn %d variabelen geschreven in file getallen.bin!", ret);
}
fclose(fp);
}
getchar();
return 0;
}
De expressie:
&rij[0]
kan vervangen worden door:
rij
omdat de naam van een array gebruikt kan worden als een pointer naar het eerste element van de array.
De uitvoer van dit programma is als volgt:
In het directory waar de executable file is uitgevoerd is nu de file getallen.bin aangemaakt. In de filebrowser kunnen de "eigenschappen" van deze file bekeken worden. De file blijkt 400 bytes groot te zijn. Met de hexeditor kunnen we de inhoud van de file zowel in hexadecimale codering als in ASCII codering bekijken.
Een binairefile kan ingelezen worden met de functie fread
. Deze
functie leest een aantal bytes uit de file en is in de include file
stdio.h
als volgt gedeclareerd:
size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
Als eerste parameter moet een pointer worden meegegeven naar de variabele
(of variabelen) die moet(en) worden ingelezen. Als tweede parameter moet
de grootte van één variabele (in bytes) worden opgegeven. Meestal
wordt de operator sizeof
gebruikt om de grootte te bepalen.
Als derde parameter moet het aantal variabelen dat moet worden ingelezen
worden meegegeven. (Het is alleen zinvol om een aantal > 1 op te geven
als de pointer ptr
wijst naar een array! Als vierde parameter
moet een FILE*
worden meegegeven die je hebt teruggekregen van
de functie fopen
.
De functie fread
geeft het aantal variabelen terug dat succesvol
is ingelezen.
In het onderstaande voorbeeldprogramma read_ints_1.c wordt de hierboven aangemaakte file getallen.bin getal voor getal ingelezen en naar het beeldscherm geschreven.
#include <stdio.h>
int main(void) {
int i;
FILE *fp;
fp = fopen("getallen.bin", "rb");
if (fp == NULL) {
printf("Kan file getallen.bin niet openen!");
}
else {
while (fread(&i, sizeof i, 1, fp) == 1) {
printf("%10d", i);
}
fclose(fp);
}
fflush(stdin);
getchar();
return 0;
}
Als je weet hoeveel getallen er maximaal in de file staan (en in dit geval weten we dat) kun je de hele file in één keer inlezen (in een array).
#include <stdio.h>
#define AANTAL 200
int main(void) {
int rij[AANTAL];
FILE *fp;
size_t i, ret;
fp = fopen("getallen.bin", "rb");
if (fp == NULL) {
printf("Kan file getallen.bin niet openen!");
}
else {
ret = fread(&rij[0], sizeof rij[0], AANTAL, fp);
if (ret == 0) {
printf("Er is iets fout gegaan bij lezen uit file getallen.bin!");
}
else {
printf("Er zijn %d variabelen gelezen uit file getallen.bin!\n", ret);
}
for (i = 0; i < ret; ++i) {
printf("%10d", rij[i]);
}
fclose(fp);
}
fflush(stdin);
getchar();
return 0;
}
Merk op dat de array 200 integers kan bevatten terwijl de file maar 100 integers
bevat. Dit is echter geen probleem omdat de functie fread
het
aantal ingelezen getallen teruggeeft.
De uitvoer van dit programma read_ints.c is als volgt:
Je kunt in een binairefile ook zoeken (voor- en achteruit spoelen). De functie
fseek
kan gebruikt worden om de filepointer te verplaatsen en
is in de include file stdio.h
als volgt gedeclareerd:
int
fseek(FILE *stream, long offset, int origin);
Als eerste parameter moet een FILE*
worden meegegeven die je
hebt teruggekregen van de functie fopen
. Als tweede parameter
moet een aantal bytes worden meegegeven dat je de filepointer wilt
verplaatsen. Als derde parameter moet de plaats in de file worden opgegeven
waarvandaan de offset
wordt opgegeven. Deze laatste parameter
mag maar 3 waarden hebben (die als symbolische constanten zijn gedefinieerd
in stdio.h
):
SEEK_SET |
De parameter offset geeft het aantal bytes vanaf het begin
van de file. In dit geval kun je een positieve waarde van offset
opgeven om de filepointer naar een bepaalde positie "door te spoelen". |
SEEK_CUR |
De parameter offset geeft het aantal bytes vanaf de huidige
positie van de filepointer. In dit geval kun je een positieve waarde van
offset opgeven om de filepointer "door te spoelen" maar ook
een negatieve waarde om de filepointer "terug te spoelen". |
SEEK_END |
De parameter offset geeft het aantal bytes vanaf het einde
van de file. In dit geval moet je een negatieve waarde van
offset opgeven om de filepointer "terug te spoelen". |
De functie fseek
geeft 0
terug als het verplaatsen
van de filepointer is gelukt.
In het onderstaande voorbeeldprogramma
seek_ints_1.c
wordt de hierboven aangemaakte
file getallen.bin
geopend en wordt de filepointer met
fseek
op het 11de getal geplaatst door 10 getallen
door te spoelen. Daarna wordt telkens met fread
1 getal gelezen
en worden met fseek
steeds 9 getallen overgeslagen.
#include <stdio.h>
int main(void) {
int i;
FILE *fp;
fp = fopen("getallen.bin", "rb");
if (fp == NULL) {
printf("Kan file getallen.bin niet openen!");
}
else {
if (fseek(fp, 10 * sizeof i, SEEK_SET) != 0) {
printf("Er is iets fout gegaan bij zoeken in file getallen.bin!");
}
else {
while (fread(&i, sizeof i, 1, fp) == 1) {
printf("%10d\n", i);
if (fseek(fp, 9 * sizeof i, SEEK_CUR) != 0) {
printf("Er is iets fout gegaan bij zoeken in file getallen.bin!");
}
}
}
fclose(fp);
}
getchar();
return 0;
}
De uitvoer van dit programma is als volgt:
Een voorbeeld met binaire files en
struct
's kun je
hier vinden.
Het verschil tussen tekstfiles en binaire files is afhankelijk van het operating systeem. In UNIX/Linux blijkt er zelfs helemaal geen verschil te zijn!
In windows is het verschil minder groot als je uit het bovenstaande verhaal
begrepen zou kunnen hebben. Het enige verschil tussen tekstfiles en binaire
files is dat bij het wegschrijven in tekstfiles het karakter
'\n'
(hexadecimaal 0A
) wordt vervangen door twee
karakters '\r'
(hexadecimaal 0D
) en
'\n'
(hexadecimaal 0A
). Bij het inlezen van een
tekstfile worden deze 2 karakters weer ingelezen als één
'\n'
karakter.
Het volgende voorbeeldprogramma tekst_vs_bin.c demonstreert dit:
#include <stdio.h>
int main(void) {
FILE *fp;
fp = fopen("tekstfile.txt", "w");
if (fp == NULL) {
printf("Kan file tekstfile.txt niet openen!");
}
else {
fprintf(fp, "Hallo\nDaar\n");
fclose(fp);
}
fp = fopen("binfile.bin", "wb");
if (fp == NULL) {
printf("Kan file binfile.bin niet openen!");
}
else {
fprintf(fp, "Hallo\nDaar\n");
fclose(fp);
}
getchar();
return 0;
}
Inhoud van tekstfile.txt:
Inhoud van binfile.bin:
Je kunt dus best met fread
en fwrite
werken in
een tekstfile maar je zult dan wel vreemde fouten krijgen omdat het wegschrijven
van een integer waar toevallig het hexadecimale byte 0A in voorkomt tot het
wegschrijven van een extra 0D zal leiden. Het volgende voorbeeldprogramma
tekst_vs_bin2.c
demonstreert dit:
#include <stdio.h>
int main(void) {
int i = 10;
FILE *fp;
fp = fopen("tekstfile.txt", "w");
if (fp == NULL) {
printf("Kan file tekstfile.txt niet openen!");
}
else {
fwrite(&i, sizeof i, 1, fp);
fclose(fp);
}
fp = fopen("binfile.bin", "wb");
if (fp == NULL) {
printf("Kan file binfile.bin niet openen!");
}
else {
fwrite(&i, sizeof i, 1, fp);
fclose(fp);
}
getchar();
return 0;
}
Inhoud van tekstfile.txt:
Inhoud van binfile.bin:
In UNIX en Linux wordt helemaal geen onderscheid gemaakt tussen tekstfiles
en binaire files. Als je een '\n'
in een file schrijft wordt
altijd slechts één karakter in de file geschreven.