Datové proudy

Zde se dozvíte, jak získat přístup k souboru („otevřít“ jej) a jak jej zase zavřít. Jak s otevřeným souborem pracovat budu popisovat v další kapitole.

Trocha opakování:

Pro práci s datovými proudy jsou definovány funkce v knihovně <stdio.h>. K datovým proudům se přistupuje pomocí struktury FILE, která je též definována v souboru <stdio.h>. Při spuštění programu jsou otevřeny tři standardní proudy stdin (standardní vstup), stdout (standardní výstup) a stderr (standardní chybový výstup).

Otevření datového proudu

Co může být záhadnějšího a mystičtějšího, než práce se soubory :-). Pomocí funkce fopen() můžete otevřít datový proud a pomocí něho pracovat s konkrétním souborem na disku. Podívejte se na deklaraci této funkce.

Písmenko f na začátku jmen funkcí (fopen(), fclose()) značí FILE. Funkce pracující s datovými proudy se tak odlišují od funkcí s přímím voláním, které budu probírat později (např. funkce open() versus fopen(), close() versus fclose() atd.).

FILE *fopen(const char *path, const char *mode);

Asi není žádné velké překvapení, že funkce fopen() vrací ukazatel na strukturu FILE. Pokud se proud nepodaří otevřít, vrací hodnotu NULL. Z jakého důvodu se nepodařilo proud uzavřít zjistíte pomocí proměnné errno definované v souboru <errno.h>.

První argument, path, je název souboru, který chcete otevřít. Ten je závislý na OS. Například v OS Windows nebo DOS to může být "C:\TEXTY\RECEPTY\buchta.txt", v Linuxu třeba "/home/rimmer/texty/recepty/výroba_zlata". Všimněte si, že se v Linuxu adresáře oddělují lomítkem, kdežto ve Windows zpětným lomítkem (backslash). V DOSu je navíc omezen název souboru na 8 písmen + 3 písmenná přípona za tečkou. Na to vše si musíte dávat pozor při psaní programu. Můžete používat také zkrácený zápis cesty k souboru, např. ..\RECEPTY\buchta.txt"?. Tomu se říká „relativní cesta“. Jistě víte, že dvě tečky reprezentují předcházející adresář vašeho aktuálního adresáře. Aktuální adresář je reprezentován tečkou, takže výraz ".\psani.txt" je to samé jako "psani.txt".

Pokud budete mít řetězec "psani.txt" jako argument parametru path, tak se funkce fopen() pokusí otevřít soubor s tímto jménem v aktuálním adresáři. Nikoliv v adresáři, ve kterém je uložený program!

Druhým argumentem je mode. Je to také textový řetězec, který může obsahovat některý z řetězců vypsaných v následující tabulce.

Pokud někde píšu „otevře soubor“, myslím tím samo sebou „otevře datový proud pro manipulaci se souborem“.

Argumenty mode pro funkci fopen()
řetězec význam Pozice v souboru
r Otevře soubor pro čtení. začátek
r+ Otevře soubor pro čtení a zápis (podobně jako "rw") začátek
w Existující soubor ořízne na nulovou délku, jinak vytvoří nový a otevře proud pro zápis začátek
w+ Jako "w", ale proud otevře i pro čtení. začátek
a Otevře soubor pro čtení. Pokud ještě neexistuje, je vytvořen. konec
a+ Otevře soubor pro čtení a zápis. Pokud soubor neexistuje, je vytvořen. konec
t Textový režim. -
b Binární režim. -

Vždy byste měli uvést, zda chcete přistupovat k souboru v textovém nebo binárním režimu. Argument mode by tedy mohl být např. "rt" nebo "rb", nebo také "rwt". Z logiky věci však vyplývá, že nelze použít zároveň textový i binární přístup k souboru. Musíte si vybrat jeden z nich. V Linuxu není rozdíl mezi textovými a binárními soubory, takže je jedno, zda použijete textový či binární přístup. V jiných OS už zde rozdíl je. Proto, kvůli přenositelnosti kódu, je dobré určit způsob přístupu k souboru i když programujete v Linuxu.
Pokud přístup k souboru neurčíte, je defaultně nastaven textový režim.

A nyní se podívejte na příklady použití funkce fopen() a co se přitom může stát.

FILE *soubor;
soubor = fopen("nazev.txt","rt");
Soubor "nazev.txt" se otevře pro čtení (v textovém režimu). Pokud se to nepodaří (soubor neexistuje nebo k němu nejsou přístupová práva), funkce fopen() vrátí NULL.
soubor = fopen("nazev.txt","wb");
Soubor "nazev.txt" se zkrátí na nulovou délku. Pokud neexistuje, vytvoří se. Pokud se to nezdaří, funkce vrátí NULL. Pokud ano, je soubor otevřen pro zápis v binárním módu.
if ((soubor = fopen("nazev.txt","rt")) == NULL) {
    printf("soubor nelze otevrit!");
    exit(1);
}
Možný způsob kontroly. Jak jistě víte, výraz přiřazení (něco=něco) lze v jazyku C použít jako hodnotu. (Touto hodnotou je hodnota přiřazená do levého operandu.)

Uzavření a vyprázdnění datového proudu

Když už víte jak souborový proud otevřít, není od věci si říct, jak (a proč) jej zavřít. K tomu slouží funkce fclose().

int fclose(FILE *stream);

Při uzavření datového proudu stream je tento proud vyprázdněn z vyrovnávací paměti na disk (nebo třeba na standardní výstup, pokud je stream stdin). Po uzavření tohoto proudu již není možné se souborem pracovat. Jedině po znovuotevření pomocí fopen().

Počet možných otevřených souborů v jeden okamžik je omezen, proto je dobré soubory, které již nechcete používat, co nejdříve zavřít. Pokud datový proud sami neuzavřete, bude automaticky uzavřen po skončení programu. V případě havárie programu před voláním fclose() se může stát, že o změny v souboru přijdete. Někdy, když otevřete soubor pro zápis, je ostatním programům operačním systémem zakázán přístup k tomuto otevřenému souboru (není možné, aby dva programy zapisovali zároveň do jednoho souboru). To je také důvod k tomu co nejdříve soubor uzavřít.

Funkce fclose() vrací nulu v případě úspěchu, jinak EOF a proměnnou errno nastaví na indikátor chyby. Např. EBADF znamená, že proud stream nebyl otevřeným datovým proudem.

Funkce fclose() může selhat z různých důvodů, například kvůli plnému disku. Pokud v programu nezkontrolujete návratovou hodnotu funkce fclose(), je to chyba, protože nemáte jistotu, že se data skutečně zapsala.

Zmínil jsem se, že při uzavření souboru dojde k vyprázdnění datového proudu. Někdy je však potřeba vyprázdnit vyrovnávací paměť dříve. K tomu slouží funkce fflush().

int fflush(FILE *stream);

Argument stream je samozřejmě datovým proudem, který chcete vyprázdnit. Pokud má stream hodnotu NULL, jsou vyprázdněny všechny datové proudy, které jsou v programu otevřené. Funkce fflush() vrací 0 v případě úspěchu, jinak EOF a proměnnou errno nastaví na příslušnou chybu.

Dovolím si upozornit, že ani funkce fclose() ani fflush() nezajistí, že budou data skutečně fyzicky na disku. Mezi programem a hardwarem je totiž ještě operační systém, který také využívá vyrovnávací paměti. O tom je třeba vědět, pokud například děláte nějaký víceuživatelský program (třeba databázi), kde k jednomu souboru přistupuje několik uživatelů naráz (třeba i ze vzdálených počítačů). V Unixu k reálnému zapsání dat na disk slouží funkce fsync().

S funkcí fflush() jste se již setkali v několika příkladech, kde jste potřebovali vyprázdnit standardní výstup stdout. Program cekej.c je jedním z nich.

Příklad

V příkladu si procvičíte otevření a uzavření souboru. Jak do souboru něco zapsat, nebo z něj něco přečíst, to se v dozvíte v dalších kapitolách.

  1. /*------------------------------------------------*/
  2. /* c31/fopen1.c                                   */
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #include <errno.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7.  
  8. #define NAZEV "pokus.prd"
  9.  
  10. #ifndef ENOENT
  11. #define ENOENT 2
  12. #endif
  13.  
  14. /* vraci 0 pokud soubor existuje,
  15.    jinak cislo chyby */
  16. int SouborExistuje(char *nazev)
  17. {
  18.     FILE *soubor;
  19.     if ((soubor = fopen(nazev, "rb")) != NULL) {
  20.         fclose(soubor);
  21.         return 0;       /* soubor existuje,
  22.                            jinak by se jej nepodarilo otevrit */
  23.     }
  24.  
  25.     if (errno == ENOENT) {
  26.         return errno;   /* soubor neexistuje */
  27.     }
  28.  
  29.     return errno;       /* soubor mozna existuje, ale nepodarilo se
  30.                            jej otevrit (treba uz je otevreno prilis
  31.                            mnoho souboru nebo nemate prava atp.) */
  32. }
  33.  
  34. int main(void)
  35. {
  36.     FILE *soubor;
  37.     char text[255];
  38.  
  39.  
  40.     printf("Soubor %s %sexistuje.\n", NAZEV,
  41.            !SouborExistuje(NAZEV) ? "" : "ne");
  42.  
  43.     /* otevre se soubor pro cteni a pokud se to zdari,
  44.        hned se zase zavre */
  45.  
  46.     if ((soubor = fopen(NAZEV, "rt")) == NULL) {
  47.         strcpy(text, "Otevreni \"rt\" souboru ");
  48.         strcat(text, NAZEV);
  49.         perror(text);
  50.     } else {
  51.         printf("Soubor %s se podarilo otevrit\n", NAZEV);
  52.         if (fclose(soubor))
  53.             perror("Zavirani souboru");
  54.     }
  55.  
  56.  
  57.     /* otevre se soubor pro aktualizaci, pokud neexistuje,
  58.        bude vytvoren. Pak se hned zavre */
  59.  
  60.     if ((soubor = fopen(NAZEV, "at")) == NULL) {
  61.         strcpy(text, "Otevreni \"at\" souboru ");
  62.         strcat(text, NAZEV);
  63.         perror(text);
  64.     } else {
  65.         printf("Soubor %s se podarilo otevrit\n", NAZEV);
  66.         if (fclose(soubor))
  67.             perror(NULL);
  68.     }
  69.  
  70.     return 0;
  71. }
  72.  
  73. /*------------------------------------------------*/
Visual Studio

Makro _CRT_SECURE_NO_WARNINGS je tu kvůli funkci fopen(), viz scanf().

Možný výstup po prvním spuštění:

Soubor pokus.prd neexistuje.
Otevreni "rt" souboru pokus.prd: No such file or directory
Soubor pokus.prd se podarilo otevrit

Možný výstup po druhém spuštění:

Soubor pokus.prd existuje.
Soubor pokus.prd se podarilo otevrit
Soubor pokus.prd se podarilo otevrit

V aktuálním adresáři, ze kterého jste program spouštěli, byste teď měli najít prázdný soubor pokus.prd.

Komentář Hlášení chyby
Created: 29.8.2003
Last updated: 24.6.2014
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..