| ← soubory | C/C++ | textové soubory → |
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).
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é budeme 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é kácený zápis souboru, např. ..\RECEPTY\buchta.txt"?. 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 (ve Windows) 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“.
| ř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 by jste 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íváme na příklady použití funkce fopen() a řekneme si, co se přitom může stát.
FILE *soubor;
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. 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.
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() jsme se již setkali v několika příkladech, kde jsme potřebovali vyprázdnit standardní výstup stdout. Program cekej.c je jedním z nich.
V příkladu si procvičíme otevření a uzavření souboru. Jak do souboru něco zapsat, nebo z něj něco přečíst, to si povíme v dalších kapitolách.
1: /*------------------------------------------------*/ 2: /* fopen1.c */ 4: #include <errno.h> 5: #include <stdio.h> 6: #include <string.h> 8: #define NAZEV "pokus.prd" 10: #ifndef ENOENT 11: #define ENOENT 2 12: #endif 14: /* vraci 0 pokud soubor existuje, 15: jinak cislo chyby */ 16: int SouborExistuje(char *nazev) 17: { 18: FILE *soubor; 19: if ((soubor = fopen(nazev, "rt")) != NULL) { 20: fclose(soubor); 21: return 0; /* soubor existuje, 22: jinak by se jej nepodarilo otevrit */ 23: } 25: if (errno == ENOENT) { 26: return errno; /* soubor neexistuje */ 27: } 29: return errno; /* soubor mozna existuje, ale nepodarilo 30: se jej otevrit (treba uz je otevreno prilis 31: mnoho souboru nebo nemate prava atp.) */ 32: } 36: int main(void) 37: { 38: FILE *soubor; 39: char text[255]; 42: printf("Soubor %s %sexistuje.\n", NAZEV, 43: !SouborExistuje(NAZEV) ? "" : "ne"); 45: /* otevre se soubor pro cteni a pokud se to zdari, 46: hned se zase zavre */ 48: if ((soubor = fopen(NAZEV, "rt")) == NULL) { 49: strcpy(text, "Otevreni \"rt\" souboru "); 50: strcat(text, NAZEV); 51: perror(text); 52: } else { 53: printf("Soubor %s se podarilo otevrit\n", NAZEV); 54: if (fclose(soubor)) 55: perror("Zavirani souboru"); 56: } 59: /* otevre se soubor pro aktualizaci, pokud neexistuje, 60: bude vytvoren. Pak se hned zavre */ 62: if ((soubor = fopen(NAZEV, "at")) == NULL) { 63: strcpy(text, "Otevreni \"at\" souboru "); 64: strcat(text, NAZEV); 65: perror(text); 66: } else { 67: printf("Soubor %s se podarilo otevrit\n", NAZEV); 68: if (fclose(soubor)) 69: perror(NULL); 70: } 72: return 0; 73: } 75: /*------------------------------------------------*/
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, by jste teď měli najít prázdný soubor pokus.prd.
| ← soubory | C/C++ | textové soubory → |
