V našich programech bychom chtěli pracovat i se soubory. Pro otevření souboru používáme funkci fopen z knihovny stdio.h. Té předáme dva argumenty. První je cesta k souboru a druhý je mód, v jakém daný soubor chceme otevřít. Módy máme následující

Mód Vysvětlení Pokud soubor existuje Pokud soubor neexistuje
r otevře soubor ke čtení (read) začne ho číst od začátku chyba
r+ otevře soubor ke čtení (read) začne ho číst od začátku chyba
w otevře soubor k zápisu (write) smaže obsah vytvoří nový soubor
w+ otevře soubor ke čtení a zápisu (write) smaže obsah vytvoří nový soubor
a otevře soubor k zápisu na konec (append) zapíše na konec vytvoří nový soubor
a+ otevře soubor ke čtení a zápisu na konec (append) zapíše na konec vytvoří nový soubor

Po otevření souboru nám funkce fopen vrátí buď NULL, pokud se nepodařilo soubor otevřít (například soubor neexistuje). Nebo vrátí ukazatel na soubor (říkáme deskriptor souboru).

Těchto otevřených deskriptorů souborů může být otevřených najednou pouze omezené množství (liší se dle operačního systému, na Windows zhruba 512 v jednom programu, ale limity se dají navýšit). Proto je potřeba soubor uzavřít, jakmile se souborem už nepotřebujeme pracovat. Pokud byste soubor zapomněli zavřít, tak se automaticky uzavře po skončení programu. Pokud jste soubor ale otevřeli pro zápis a nezavřeli ho před skončením programu, tak se může stát, že ztratíte data, které jste do souboru zapsali.

Mějme program, který otevře soubor C:\Users\tomas\test.txt ke čtení a přečte z něj každý znak a každý znak vytiskne.

#include <stdio.h>

int main() {
    FILE * soubor = fopen("C:\\Users\\tomas\\test_cteni.txt", "r");

    if (soubor == NULL) {
        printf("Soubor se nepodarilo otevrit\n");
        return 1;
    }

    char c = getc(soubor);

    while(c != EOF) {
        printf("%c ", c);
        c = getc(soubor);
    }
 
    fclose(soubor);

    return 0;
}

Setkáváme se zde s novou funkcí a konstantou. Funkce getc nám vrátí jeden znak z otevřeného souboru a posune deskriptor o jeden znak dále. To znamená, že jakmile zavoláme funkci poprvé, tak se nám vrátí první znak soubor. Jakmile ji zavoláme podruhé, tak se vrátí už druhý znak atd. Pokud jsme dočetli soubor až na konec, tak funkce vrátí konstantu EOF.

Všimněme si, že pokud je soubor NULL, tak ukončujeme program s return 1 místo klasického return 0. Obecně v programech platí, že pokud program skončí úspěšně, tak by měl vrátit hodnotu 0. Pokud dojde k chybě, tak by měl vrátit cokoliv jiného, než je 0. V našem případě tedy 1.

Různé hodnoty můžou znamenat různé chybové kódy. Například

  • 1 - nepodařilo se načíst soubor (return 1)
  • 2 - soubor je prázdný (return 2)
  • 3 - neznámá chyba (return 3)

Díky těmto chybovým kódům dokážeme dát uživateli extra informaci, proč náš program nefunguje tak, jak by uživatel chtěl.

Zápis do souboru

Pokud bychom chtěli zapisovat do soubor, tak nám stačí soubor otevřít k zápisu. K samotnému zápisu můžeme použít funkci putc z knihovny stdio.h. Ta vrací znak, který jsme zapsali a v případě chyby vrací EOF. První parametr je znak, který chceme zapsat a druhý parametr je deskriptor souboru.

#include <stdio.h>

int main() {
    FILE * soubor = fopen("C:\\Users\\tomas\\test_zapis.txt", "w");

    if (soubor == NULL) {
        printf("Soubor se nepodarilo otevrit\n");
        return 1;
    }

    char * text = "Mame radi jazyk C\n";

    for (int i = 0; text[i] != '\0' ; i = i + 1) {
        char vysledek = putc(text[i], soubor);
        if (vysledek == EOF) {
            printf("Nepodarilo se ulozit znak\n");
            fclose(soubor);
            return 1;
        }
    }
 
    fclose(soubor);

    return 0;
}

Když ukládáme data do souboru, tak je dobré uložit jako poslední znak \n (nový řádek). Ničemu to asi extra nevadí, když to tak neuděláme, ale některé nástroje pro zpracování textu pak lépe fungují.

Pokud bychom nechtěli zapisovat po jednom znaku, tak můžeme použít funkci fprintf. Ta vrátí počet zapsaných znaků nebo záporné číslo, pokud došlo k chybě.

#include <stdio.h>

int main() {
    FILE * soubor = fopen("C:\\Users\\tomas\\test_zapis.txt", "w");

    if (soubor == NULL) {
        printf("Soubor se nepodarilo otevrit\n");
        return 1;
    }

    int vysledek = fprintf(soubor, "Mame radi jazyk C\n");
    fclose(soubor);

    if (vysledek < 0) {
        printf("Nepodarilo se ulozit text\n");
        return 1;
    }

    return 0;
}

Binární a textové soubory

V programování se bavíme o dvou typech souborů. Binární a textové. Na úrovni uložení dat jsou to pořád jedničky a nuly. Jediný rozdíl je jenom v tom, že kdybyste si soubory otevřeli v textovém editoru, tak binární soubor nedokážete přečíst, ale textový soubor je čitelný.

Je to rozdělení spíše pro lidi. Počítačům je to jedno, jestli pracují s binárním nebo textovým souborem. V jazyku C to však má vliv na konce řádků viz níže.

CRLF vs LF

Konce řádku (anglicky line endings) nejsou stejné na všech operačních systémech. Pro Linux se používá znak \n (v ASCII tabulce znak 10 - LF - Line Feed). Starý Mac OS měl pro označení konce řádku znak \r (v ASCII tabulce znak 13 - CR - Carriage Return). Windows dokonce používá pro označení konce řádků dva znaky \r\n.

Jedná se o věc, která vznikla z historického hlediska, kdy jsme ještě psali na psacích strojích. Tam Line Feed (LF) znamenal posuň psací stroj o jeden řádek dolů a Carriage Return (CR) znamenal vrať kurzor doleva na začátek papíru.

Dnešní operační systémy jsou už chytřejší a snaží se pracovat s oběma typama konce řádků, tzv. s CRLF i LF. Samotný \r (jak to měl starý Mac OS) se dnes již nepoužívá.

Když uložíme do souboru \n na Windows, tak se ve skutečnosti uloží \r\n, protože C ví, že je na Windows.

Pokud bychom chtěli zapsat na Windows jenom znak \n bez automatického \r, tak bychom museli otevřít soubor jako binární. To se dělá tak, že k módu přidáme ještě znak b, tzv. pro zápis bychom napsali "wb" místo "w". Poté když uložíme znak \n, tak se uloží opravdu jen tento znak i na Windows.

Odkazy

Následující kapitola: Práce s argumenty programu

GitHub diskuze k této kapitole

Zpátky na přehled

Předchozí kapitola: Struktury