3. díl - Binární soubory a neformátovaný text

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

V minulém díle jsme se soustředily 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);
}
Čtení a zápis do binárního souboru v C

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ím článku.


 

Stáhnout

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

 

  Aktivity (2)

Článek pro vás napsal patrik.valkovic
Avatar
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu.

Jak se ti líbí článek?
Celkem (1 hlasů) :
55555


 



 

 

Komentáře

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.

Zatím nikdo nevložil komentář - buď první!