Obsah binárního souboru
V binárních souborech jsou data zapsána ve stejné podobě, jako jsou v paměti.
Pokud bychom si vzali hodnotu 123456 typu int
a zapíšeme ji do
textového souboru, tak se napíše řetězec "123456". Pokud zapíšeme hodnotu v binárním
módu,tak se zapíšou 4 bajty 0...11110001001000000.
Při otevírání binárních souborů používáme navíc v módu b.
Výhody a nevýhody binárních souborů
Binární soubory typicky zabírají méně místa než nekomprimovaný textový soubor. Práce s binárními soubory je rychlejší: např. nepotřebujeme znaky ”123456” převádět na číslo. Bohužel ne vždy jsou přenositelné na jiný počítač kvůli různě velkým typům.
Zápis binárních dat
K zápisu binárních dat slouží funkce fwrite
, která vrací
počet úspěsně zapsaných prvků a má hlavičku:
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
// ptr - Ukazatel na data která se mají zapsat do souboru.
// size - Velikost v bajtech jednotlivých prvků k zápisu.
// count - Počet prvků (každý velikosti size) určených k zápisu.
// stream - Soubor pro zápis.
Příklad zápisu
int numbers[] = {-1, 1, 9, 15, 16, 31};
// otevreme soubor pro zapis binarnich dat
FILE* file = fopen("numbers.dat", "wb");
// zapiseme 6 polozek typu (velikosti) int,
// ktera zacinaji na adrese "numbers" (=zacatek pole)
fwrite(numbers, sizeof(int), 6, file);
fclose(file);
Obsah souboru
Binární soubor nemá smysl otevírat v běžném textovém editoru. Ale existují editory, které binární soubory zobrazí v hexa zápise. Po zápisu čísel -1, 1, 9, 15, 16, 31 bychom tak mohli dostat:
ffff ffff 0100 0000 0900 0000 0f00 0000
1000 0000 1f00 0000
Příklad zápisu jedné proměnné
double number = 3.14;
FILE* file = fopen("numbers.dat", "wb");
// Pozor, musime predat adresu. Identifikator pole je adresa
// ale u bezne promenne musime adresu vynutit operatorem &
// Pocet nastavime na 1, protoze ukladame 1 polozku.
fwrite(&number, sizeof(double), 1, file);
fclose(file);
Čtení binárních dat
Ke čtení binárních dat slouží funkce fread
, která vrací
počet úspěsně zapsaných prvků a má hlavičku:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
// ptr - Je adresa paměti, kam se mají data načíst.
// size - Udává velikost jednoho přečteného prvku.
// count - Je počet přečtených prvků.
// soubor - Je ukazatel na soubor, ze kterého se čte.
Příklad čtení dat
// Obsah souboru: -1, 1, 9, 15, 16, 31
#define SIZE 6
int loaded_numbers[SIZE];
//nebo
int *loaded_numbers = (int*) malloc(SIZE * sizeof(int));
// otevreme pro cteni v binarnim rezimu!
FILE* file = fopen("numbers.dat", "rb");
// Precteme 6 polozek typu int do pole nactena_cisla
// nactena_cislaje ukazatel na zacatek pole
fread(loaded_numbers, sizeof(int), SIZE, file);
// Vytiskne se: -1, 31
printf("%i, %i\n", loaded_numbers[0], loaded_numbers[5]);
fclose(file);
Příklad čtení jedné proměnné
// V souboru je 3.14
// Otevreme soubor pro cteni v binarnim rezimu
FILE* file = fopen("number.dat", "rb");
// Promenne, do ktere nacteme cislo
double number;
// Musime predat adresu, kam to nacteme
// Nacteme jednu polozku o velikost double
fread(&number, sizeof(double), 1, file);
// Vypise se 3.14
printf("%g\n", number);
fclose(file);
Uložení/čtení více proměnných
int number = 213, loaded_number;
char string[] = "ahoj", loaded_string[5];
// zapis do souboru
FILE *file = // ... Otevreni souboru pro zapis
fwrite(&number, sizeof(int), 1, file); // zapiseme int
fwrite(string, sizeof(char), 5, file); // zapiseme reteze
fclose(file);
// soubor ma velikost 4 + 5 = 9 bajtu
// obsah: <int><char><char><char><char><char>
// zpetne precteme
file = // ... Otevreni souboru pro cteni
fread(&loaded_number, sizeof(int), 1, file); // precteme int
fread(loaded_string, sizeof(char), 5, file); // precteme reteze
fclose(file);
printf("%i, %s\n", loaded_number, loaded_string);
Posun pozice v souboru
Pomocí funkce fseek
můžeme hýbat s aktuálním pozicí v souboru.
int fseek(FILE * stream, long int offset, int origin);
// offset - Počet bajtů (může být i záporné číslo) o kolik se má změnit aktuální pozice.
// origin - Udává výchozí pozici. Můžeme použít konstanty:
// SEEK_SET – od začátku souboru
// SEEK_CUR – od aktuální pozice
// SEEK_END – od konce souboru
Příklad
// Obsah souboru: -1, 1, 9, 15, 16, 31
FILE* file = fopen("numbers.dat", "rb");
int number;
// Posuneme se o dva inty dopredu od zacatku
fseek(file, 2 * sizeof(int), SEEK_SET);
fread(&number, sizeof(int), 1, file);
printf("%i\n", number); // 9
// Posuneme se o jeden int dopredu od aktualni pozice
fseek(file, sizeof(int), SEEK_CUR);
fread(&number, sizeof(int), 1, file);
printf("%i\n", number); // 16
Posun zpět
// Obsah souboru: -1, 1, 9, 15, 16, 31
FILE* file = fopen("numbers.dat", "rb");
int number;
// Vratime se zpet o jeden int
fseek(file, -sizeof(int), SEEK_END);
// precteme posledni int v souboru
fread(&number, sizeof(int), 1, file);
// vytiskne se 31
printf("%i\n", number);
Zjištění pozice
// Obsah souboru: -1, 1, 9, 15, 16, 31
FILE* file = fopen("numbers.dat", "rb");
int number;
// Vratime se zpet o jeden int
fseek(file, -sizeof(int), SEEK_END);
// precteme posledni int v souboru
fread(&number, sizeof(int), 1, file);
// vytiskne se 31
printf("%i\n", number);
Funkce ftell
vrací aktuální pozici v souboru.
long ftell(FILE* stream);
Příklad
FILE* file = fopen("number.dat", "rb");
int number;
// vytiskne 0, jsme na zacatku
printf("%lu\n", ftell(file));
fread(&number, sizeof(int), 1, file);
// Vytiskne 4, posunuli jsme se o 1 int (pokud ma int 4 B)
printf("%lu\n", ftell(file));
fseek(file, -sizeof(int), SEEK_END);
// Vytiskne 20
printf("%lu\n", ftell(file));
fclose(file);
Standardní proudy
Funkce printf
tiskne na tzv. standardní výstup. To není nic jiného,
než ukazatel na soubor, tj. FILE
*, který je definovaný v stdio.h
:
// Standardní vstup
FILE* stdin;
// Standardní výstup
FILE* stdout;
// Standardní chyba
FILE* stderr;
// Tyto zápisy jsou tak totožné:
printf("%i, %g\n", 10, 5.47);
fprintf(stdout, "%i, %g\n", 10, 5.47);