Lekce 3 - Binární soubory a neformátovaný text

C a C++ Céčko Práce se soubory Binární soubory a neformátovaný text

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Práce s textovými soubory (txt) v jazyce C, jsme se soustředili na soubory textové. Zjistili jsme, že vstup i výstup je naprosto totožný s operacemi, které jsme prováděli na konzoli. Na rozdíl od konzole ale můžeme vstup číst i v binární podobě. Dnes si řekneme, jak můžeme číst a zapisovat binárně.

Pro otevření souboru provedeme jedinou změnu. Jako druhý parametr přidáme do řetězce písmeno "b", kterým řekneme, že chceme se souborem pracovat binárně. Například otevření souboru pro binární čtení by vypadalo následovně:

FILE* soubor = fopen("soubor.bin","rb");

fread

Funkce fread slouží k binárnímu čtení ze souboru. Má 4 parametry:

  • Blok paměti, kam se mají data uložit. Musí být dostatečně velký, aby se do něj data vešly.
  • Velikost jednoho elementu, který se má přečíst. Zde můžeme použít například sizeof(int), pokud se chystáme číst celé čísla.
  • Počet elementů, které chceme přečíst. Protože jsme velikost jednoho elementu nadefinovali v předchozím parametru, jedná se skutečně o počet, ne o velikost.
  • Ukazatel na strukturu FILE, ze které budeme data číst.

Návratovou hodnotou funkce je počet přečtených elementů. Pokud je soubor kratší, tak se může jednat o libovolné číslo od 0 až po hodnotu předanou třetím parametrem.

fwrite

Funkce fwrite má naprosto stejné parametry, jako fread. Jediný rozdíl je v prvním parametru. Tento blok paměti již musí být naplněný hodnotami, protože se jedná o část paměti, která se do souboru zapíše. Návratovou hodnotou je počet úspěšně zapsaných elementů do souboru.

Pro ukázku napíšeme program, který přečte od uživatele 5 čísel, uloží je binárně do souboru a následně je zpět přečte.

int main(int argc, char** argv)
{
    //přečteme čísla
    int cisla[5];
    printf("Zadejte serii 5 cisel:");
    scanf(" %d %d %d %d %d", cisla, cisla + 1, cisla + 2, cisla + 3, cisla + 4);

    //uložíme čísla do souboru
    FILE* soubor_k_zapisu = fopen("soubor.bin", "wb");
    fwrite(cisla, sizeof (int), 5, soubor_k_zapisu);
    fclose(soubor_k_zapisu);

    //vynulujeme paměť
    memset(cisla,0,sizeof(int)*5);

    //přečteme čísla ze souboru
    FILE* soubor_ke_cteni = fopen("soubor.bin", "rb");
    fread(cisla, sizeof (int), 5, soubor_ke_cteni);
    fclose(soubor_ke_cteni);

    //vypíšeme čísla
    for (int i = 0; i < 5; i++)
        printf("Precteno: %d\n", cisla[i]);

    return (EXIT_SUCCESS);
}

Výsledek:

Konzolová aplikace
{TTILE}BinarniSoubory
Zadejte serii 5 cisel:12 15 48 65 12
Precteno: 12
Precteno: 15
Precteno: 48
Precteno: 65
Precteno: 12

Endianita

S binárními soubory souvisí endianita. Jedná se o způsobu uložení dat v paměti počítače. U big endianu se nejvyšší bit uloží na nejnižší adresu, zatímco u little endianu se nejvyšší bit uloží na adresu nejvyšší. Budeme chtít uložit číslo 0x4A3B2C1D.

adresa paměti 0x100 0x101 0x102 0x103
hodnota pro little endian 1D 2C 3B 4A
hodnota pro big endian 4A 3B 2C 1D

Je potřeba počítat s přenosem souborů mezi platformami. Přesuneme-li soubor z platformy little endian na platformu big endian, budou bajty zapsány v opačném pořadí. Dá se tomu vyhnout definováním specifických hodnot, které se čtou z obou stran stejně. Například 0x46464646 by mohl označovat little endian a 0x73737373 big endian. Je jedno, z jakého směru se číslo přečte, výsledek bude vždy stejné. Umístíme-li tuto konstantu například na začátek souboru, okamžitě víme, v jakém formátu je uložen celý soubor.

Neformátovaný vstup a výstup

Nyní se vrátíme zpět k textovým souborům. Jak jsme si řekli, čtení a zápis formátovaného textu probíha stejně jako u konzole. Jediná změna je v předponě "f" na začátku funkce a na prvním parametru, kdy se u práce se soubory jako první parametr posílá ukazatel na strukturu FILE. U neformátovaného textu tomu není jinak. Pro získání jednoho znaku máme funkci fgetc, pro získání celého řádku fgets. Pro zápis jednoho znaku máme funkci fputc a pro zápis celého řádku zase funkci fputs. Bližší fungování těchto funkcí nebudu rozebírat, vše je popsané v článku o pokročilém vstupu a výstupu.

Souvislosti s konzolovým vstupem a výstupem

Určitě jste si všimli podobných názvů funkcí pro jednotlivé operace. Ve skutečnosti nám dobře známe funkce (printf, scanf a další) interně volají funkce pro manipulaci se soubory. Hlavičkový soubor stdio.h definuje tři proměnné: stdin, stdout a stderr. Všechny tři pracují s konzolí a lze je využít namísto ukazatelů na soubor. Příklad výše tedy můžeme přepsat do podoby obsahující pouze operace pro manipulaci se soubory.

int main(int argc, char** argv)
{
    //přečteme čísla
    int cisla[5];
    fprintf(stdout, "Zadejte serii 5 cisel:");
    fscanf(stdin, " %d %d %d %d %d", cisla, cisla + 1, cisla + 2, cisla + 3, cisla + 4);

    //uložíme čísla do souboru
    FILE* soubor_k_zapisu = fopen("soubor.bin", "wb");
    fwrite(cisla, sizeof (int), 5, soubor_k_zapisu);
    fclose(soubor_k_zapisu);

    //vynulujeme paměť
    memset(cisla, 0, sizeof (int)*5);

    //přečteme čísla ze souboru
    FILE* soubor_ke_cteni = fopen("soubor.bin", "rb");
    fread(cisla, sizeof (int), 5, soubor_ke_cteni);
    fclose(soubor_ke_cteni);

    //vypíšeme čísla
    for (int i = 0; i < 5; i++)
        fprintf(stdout, "Precteno: %d\n", cisla[i]);

    return (EXIT_SUCCESS);
}

Z toho také plyne jeden zajímavý poznatek. Velká část postupů, které byly nebo budou dále uváděny pro soubory, lze bez problémů aplikovat i na standardní vstup a výstup (tedy na konzoli).

Ne všechny soubory jsou lineární (nechceme je číst celé od začátku po konec). Bylo by užitečné se v souboru pohybovat. Na to se podíváme v příští lekci, Přesun v souboru a implementace v operačním systému .


 

Stáhnout

Staženo 10x (46.18 kB)
Aplikace je včetně zdrojových kódů v jazyce C

 

 

Článek pro vás napsal patrik.valkovic
Avatar
Jak se ti líbí článek?
1 hlasů
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity (4)

 

 

Komentáře

Avatar
Student C
Člen
Avatar
Student C:21. dubna 21:05

je to dobrý článek ale moc mi neříká jestli se dá psát do binsouboru i řetězce atd?

 
Odpovědět 21. dubna 21:05
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Student C
DarkCoder:21. dubna 22:06

V článku je to uvedeno. Binární zápis do souboru se provádí pomocí funkce fwrite() jejíž první parametr představuje blok paměti, který se má do souboru zapsat. Tedy blokem paměti může být např. ukazatel na jednorozměrné pole znaků, ve kterém máš uložený požadovaný řetězec.

Odpovědět 21. dubna 22:06
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Avatar
Peter Mlich
Člen
Avatar
Odpovídá na Student C
Peter Mlich:23. dubna 11:50

ZapisData vola funkci prenesData.
prenesData(sou­rce,[a-b,c-d,e-f],destination,[g-h,i-j])
Data prenasi ze zarizeni 1 do zarizeni 2. Treba pri kopirovani souboru je adresa zarizeni stejna. Prenasi data z adres na zarizeni a-b, c-d, e-f na jine adresy. Prenasi to jako bajty.
Je pak na tobe, tvurci programu nebo programovaciho jazyka, jestli si nad tim postavis specialni funkce. Podstatne je, ze prenesData potrebuje adresy, ze kterych ma bajty vykopirovat.
Seznam zarizeni ziskavas pri startu pc. Pridelujes adresu disku, pameti, sitove karte, graficke, ...

Cili, fwrite je pouze primitivni funkce, ktera posila data z pameti bajt po bajtu do zarizeni disk.

 
Odpovědět 23. dubna 11:50
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 3 zpráv z 3.