2. díl - Práce s textovými soubory (txt) v jazyce C

C++ Práce se soubory Práce s textovými soubory (txt) v jazyce C

V minulém dílu našeho seriálu o programování v jazyce C jsme si udělali úvod do práce se soubory. V dnešním dílu se naučíme pracovat s textovými soubory, které jsou nejjednodušší a také je asi budeme využívat nejčastěji. Do textových souborů zapisujeme nebo je čteme po řádkách. Lze je samozřejmě číst/zapisovat i po znacích, ale řádkový přístup je určitě lidštější. K zápisu do souboru budeme používat funkci fprintf() a ke čtení fscanf(). Dvojice funkcí funguje úplně stejně jako printf() a scanf(), které pracovali se standardním vstupem/výstupem (konzolí). Tyto funkce ovšem pracují se souborem. Odkaz na soubor předáme vždy jako první parametr funkce.

Otevření souboru

Předtím, než se souborem začneme pracovat, musíme ho otevřít. K tomu slouží funkce fopen(). Při otevírání souboru specifikujeme mód, pro který soubor otevíráme. Nejčastěji je to "w" (zápis), "r" (čtení) nebo "a" (připisování).

Zavření souboru

Po dokončení práce se souborem ho je třeba zavřít. Asi byste uhodli, že k tomu slouží funkce fclose(). Pokud bychom soubor zapomněli zavřít, chápal by operační systém jako že s ním stále pracujeme. Ostatní programy by ho nemohly používat a také bychom zbytečně plýtvaly zdroji, protože operační systém má na počet najednou otevřených souborů většinou nějaké limity. V neposlední řadě bychom si měli sdělit, že práce se soubory používá tzv. buffer. Zapisovat totiž znak po znaku by disk velmi vytížilo, proto se čeká, až se naplní určitá vyrovnávací paměť, která se potom najednou na disk zapíše a tím se ušetří přístupy k disku. Kdybychom zapomněli zavolat fclose(), mohl by ve vyrovnávací paměti zůstat ještě nějaký text, který by se do souboru nedopsal.

Zápis do souboru

Uveďme si konečně příklad a to vytvoření jednoduchého textového souboru s dopisem. Záhy si ho vysvětlíme:

int main(int argc, char** argv) {
    FILE * p_soubor = fopen("dopis.txt", "w");
    if (p_soubor == NULL)
    {
        printf("Soubor se nepodařilo otevřít pro zápis, zkontrolujte prosím oprávnění.");
        return 1;
    }

    fprintf(p_soubor, "Drahá Brynn,\n");
    fprintf(p_soubor, "opatruj se, Malcolm unikl a jistě si pro mne brzy přijde jako pro prvního.\n");
    fprintf(p_soubor, "Musíš navést Brandona, dovést ho k amuletu, klíčem k zaříkávadlu by možná\n");
    fprintf(p_soubor, "mohla být levandulová růže.\n\n");
    fprintf(p_soubor, "Kallak\n");

    if (fclose(p_soubor) == EOF)
    {
        printf("Soubor se nepodařilo uzavřít.");
        return 1;
    }

    return (EXIT_SUCCESS);
}

Na začátku zavoláme funkci fopen(), které sdělíme, že chceme otevřít soubor dopis.txt v módu pro zápis. Pokud soubor neexistuje, bude vytvořen. Pokud již existuje, bude přepsán! Funkce vrací ukazatel na strukturu typu FILE. Právě přes tento ukazatel budeme se souborem dále pracovat a proto si ho uložíme.

Soubor se nemusí podařit otevřít a to hlavně tehdy, když k němu nemá program přístup. Stane se tak, když program např. spustíme z disku CD, kam nelze zapisovat, nebo když ho spustíme např. přímo na disku C (mimo uživatelské složky jako jsou dokumenty nebo plocha, kam máme povoleno zapisovat). Při této situaci vrátí NULL (tedy prázdný ukazatel) a na tuto hodnotu bychom měli reagovat.

Prostřední část programu je triviální, pouze zapisujeme do souboru řádky textu pomocí fprintf(). Samozřejmě bychom mohli zapisovat např. i proměnné a to takto:

int hlasitost = 100;
fprintf(p_soubor, "hlasitost=%d", hlasitost);

Stejně, jako se nemusí povést otevření souboru, nemusí se podařit ani jeho uzavření. Funkce fclose() v tomto případě vrátí hodnotu EOF (jako End Of File).

Když aplikaci spustíme, vytvoří se v aktuální složce s programem soubor dopis.txt s následujícím obsahem:

Vytvoření textového souboru v jazyce C

Čtení ze souboru

Čtení ze souboru je velmi obdobné. Napíšeme si opačnou aplikaci, která dopis načte a vypíše na obrazovku. Budeme předpokládat, že nevíme, kolik má řádků a proto si ukážeme jak načíst všechny řádky ze souboru.

int main(int argc, char** argv) {
    FILE * p_soubor = fopen("dopis.txt", "r");
    if (p_soubor == NULL)
    {
        printf("Soubor se nepodařilo otevřít pro čtení, zkontrolujte prosím zda existuje.");
        return 1;
    }

    char buffer[1024];
    while (fscanf(p_soubor, " %1023[^\n]", buffer) != EOF)
    {
        printf("%s\n", buffer);
    }

    if (fclose(p_soubor) == EOF)
    {
        printf("Soubor se nepodařilo uzavřít.");
        return 1;
    }

    return (EXIT_SUCCESS);
}

Otevření souboru je stejné až na záměnu módu z "w" (write) na "r" (read). Nyní budeme potřebovat pomocnou proměnnou, tzv. buffer, do kterého nám bude fscanf() řádku ukládat. Buffer se dělá dostatečně dlouhý, většinou 1024 znaků. Samotné čtení řádky je umístěno ve while cyklu a pokračuje dokud fscanf() nevrátí hodnotu EOF. Ta signalizuje, že jsme již dosáhli konce souboru. Formátovací řetězec je třeba upravit, jinak by se skenování zastavovalo o bílé znaky (mezery) a my nechceme, aby se zastavovalo jen na konci řádků (\n) a my tak četli najednou celé řádky. Načtenou řádku z bufferu vypíšeme a pokračujeme znovu. Na konci soubor uzavřeme.

Výsledek:

Čtení z textového souboru v jazyce C

Funkce fscanf() má bohužel tu vlastnost, že spolyká prázdné řádky v souboru a my o nich ani nebudeme vědět. Někdy nás to netrápí, ale někdy bu nám to mohlo vadit. Proto se pro čtení ze souboru někdy používá funkce fgets(), která načte celý řádek až do konce. Abychom to neměli moc jednoduché, vrací funkce řádek i s jeho ukončením, tedy se znakem \n. Ten nám někdy nevadí (např. zde), ale někdy bychom ho museli odstraňovat. Pro použití fgets() by se while cyklus změnil na následující podobu:

while (fgets(buffer, sizeof(buffer), p_soubor) != NULL)
{
        printf("%s", buffer);
}

Výsledek:

Čtení z textového souboru v jazyce C

Za zmínku stojí, že k funkci fgets() existuje i funkce fputs(), která do souboru zapíše celou řádku. Její výhodou je, že za ni automaticky dosadí \n a my ho už nemusíme připojovat. Možná vás napadlo, zda neexistují i funkce gets() a puts() pro standardní vstup a výstup. Ano, můžeme je používat místo printf() a scanf().

Připsání k souboru

Na konec si ještě ukažme, jak k existujícímu souboru něco připsat. Protože kód spočívá v podstatě jen v záměně mód "w" za "a" (append), kód si ani nemusíme vysvětlovat:

int main(int argc, char** argv) {
    // Otevření souboru pro připsání
    FILE * p_soubor = fopen("dopis.txt", "a");
    if (p_soubor == NULL)
    {
        printf("Soubor se nepodařilo otevřít pro připsání, zkontrolujte prosím oprávnění.");
        return 1;
    }

    // Zápis řádek do souboru
    fprintf(p_soubor, "\nPS: Vyžádej pomoc od Darma a Zanthie pro vybudování sil amuletu.\n");

    // Uzavření souboru
    if (fclose(p_soubor) == EOF)
    {
        printf("Soubor se nepodařilo uzavřít.");
        return 1;
    }

    return (EXIT_SUCCESS);
}

A když si otevřeme soubor v textovém editoru:

Připsání do textového souboru v jazyce C

Pokročilejší práce s textovými soubory (zejména parsování formátu CSV) je ukázána v článcích Evidence osob v jazyce C - Zadávání a ukládání osob do CSV a Evidence osob v jazyce C - Načítání a vyhledávání osob.


 

Stáhnout

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

 

  Aktivity (2)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

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


 


Miniatura
Všechny články v sekci
Práce se soubory v jazyce C

 

 

Komentáře

Avatar
lastp
Redaktor
Avatar
lastp:

Funkce puts automaticky na konec přidává znak \n, ale funkce fputs znak \n nepřidává.

 
Odpovědět 28.11.2014 19:47
Avatar
ra3sk
Člen
Avatar
ra3sk:

Ahoj, používam MAC OS X a nejde mi práca so súborom.

 
Odpovědět  -2 20.11.2015 21:33
Avatar
Prasopes
Člen
Avatar
Odpovídá na ra3sk
Prasopes:

Ahoj, sedim v autě a netočí se mi kola.

 
Odpovědět  +5 24.12.2015 19:18
Avatar
Odpovědět  +1 25.12.2015 0:41
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Richard H.
Redaktor
Avatar
Odpovídá na ra3sk
Richard H.:

Když si takhle nekonkrétní tak ti nikdo nepomůže. Možná pokud napíšeš co to přesně hází za chybu tak bych ti možná byl schopen pomoct a když pošleš i kód co nefunguje tak to bude ještě lepší.

Odpovědět  +1 25.12.2015 13:17
Malý užitečný manuál je vždy lepší než bichle k ničemu.
Avatar
mkub
Redaktor
Avatar
Odpovídá na Richard H.
mkub:

mam pocit, ze je to znama firma, ktora rada tu dava podobne otazky, ako je tato...

 
Odpovědět 25.12.2015 15:07
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 6 zpráv z 6.