[Přednáška 5] | [Obsah] | [Přednáška 7] |
stdin, stdout, stderr
, funkce perror()
FILE
, nesoucí informace o otevřeném souboru
(tzv. file descriptor, popisovač souboru); struktura je uložena v datových strukturách operačního systému.
Všechny datové typy a funkce vztahující se k souborům jsou deklarovány
v hlavičkovém souboru stdio.h
.
Soubor deklarujeme jako proměnnou typu ukazatel na FILE
, např.:
FILE *vstupni;
FILE *fopen(const char *filename, const char *mode);Funkce vrací ukazatel na nově vytvořenou datovou strukturu
FILE
. Nelze-li soubor otevřít, vrátí funkce hodnotu NULL
.
Parametr filename
je jméno souboru, které může být uvedeno včetně cesty (syntaxe je pak závislá na
operačním systému, pod kterým je program překládán; zvláště u systémů MS Windows je nutné dávat pozor na zápis znaku
lomítko jako \\
v řetězcových konstantách). Parametr mode
specifikuje mód otevření souboru.
Módy uvádí tabulka 1.
Mód | Význam |
---|---|
r | Read - otevře soubor pouze pro čtení |
w | Write - vytvoří soubor pouze pro zápis. Pokud soubor neexistuje, je vytvořen prázdný, pokud existuje, je smazán a přepsán. |
a | Append - otevře soubor pro připojení. Pokud soubor existuje, je otevřen pro zápis, ale obsah není smazán. Aktuální pozice je nastavena na konec souboru, tj. zapisovaná data jsou přidána za konec souboru. Neexistuje-li soubor, je vytvořen prázdný. |
r+ | Otevře existující soubor pro čtení i zápis (změnu). |
w+ | Vytvoří nový soubor pro čtení i zápis. Jestliže soubor existuje, je přepsán. |
a+ | Otevře existující soubor pro čtení i zápis; aktuální pozici nastaví na konec souboru. |
Tabulka 1: Módy otevření souboru
Ke znakům určujícím otevření souboru pro čtení/zápis je možné připojit znak b
nebo t
, specifikující
otevření v textovém nebo binárním módu. Chybí-li písmeno b
nebo t
, je soubor
otevřen v textovém módu (viz binární a textové soubory).
Příklady:
Kód pro otevření souboru seznam.txt (pro čtení v textovém módu) v jazyce C i s ošetřením chyby zapíšeme např. takto:"rt"
otevře soubor pro čtení v textovém módu,"wb"
vytvoří soubor pro zápis v binárním módu,"wt+"
nebo"w+t"
vytvoří soubor pro čtení i zápis v textovém módu.
FILE *vstupni; vstupni=fopen("seznam.txt","rt"); if (vstupni==NULL) { printf("Chyba: soubor seznam.txt nelze otevrit"); return -1; }Efektivnější kód přiřadí ukazatel rovnou v podmínce:
FILE *vstupni; if ((vstupni=fopen("seznam.txt","rt"))==NULL) { printf("Chyba: soubor seznam.txt nelze otevrit"); return -1; }
Soubor se zavírá voláním funkce
int fclose(FILE *stream);Parametrem funkce je ukazatel na otevřený soubor. Obsah všech vyrovnávacích pamětí je zapsán před uzavřením do souboru. Funkce vrací hodnotu 0, jestliže se podařilo soubor zavřít, v případě chyby vrací hodnotu
EOF
(End of File), která
je definována jako symbolická konstanta v knihovně stdio.h
(zpravidla -1). Tato konstanta se používá jako návratová hodnota pro signalizaci
chyb u většiny funkcí pracujících se soubory.
Soubor seznam.txt
otevřený v předchozím odstavci uzavřeme: fclose(vstupni);
int fputc(int c, FILE *stream);V případě úspěšného zápisu vrací funkce zapisovaný znak
c
, pokud dojde k chybě, pak hodnotu EOF
.
Ke čtení jednoho znaku z textového souboru slouží funkce
int fgetc(FILE *stream);V případě úspěšného načtení znaku vrací funkce přečtený znak konvertovaný na typ
int
(bez znaménkového rozšíření).
Na konci souboru nebo při výskytu chyby vrací EOF
.
Pro vrácení znaku do souboru existuje funkce int ungetc(int c, FILE *stream);
'\n'
,
tj. if (c == '\n') ...
.
U textových souborů je možné testovat jejich konec dvěma způsoby. Jeden z nich je test, zda se návratová hodnoty funkce pro čtení dat
(fgetc, fscanf
) rovná hodnotě EOF
. Druhý způsob představuje volání funkce
int feof(FILE *stream);Funkce vrací nenulovou hodnotu (true), pokud bylo dosaženo konce souboru, jinak 0. Pro binární soubory (viz dále) je vhodnější používat tuto funkci
feof
, u textových je efektivnější test návratové hodnoty funkcí na hodnotu EOF
. Poznámka: Zmíněné funkce pro čtení znaku lze použít i pro binární soubory (čtou soubor po slabikách), ale nastává problém při čtení a zápisu se znakem (znaky) konce řádku. Je možné, že data v binárním souboru budou taková, že funkce fgetc vrátí hodnotu -1 uprostřed souboru).
FILE *vstupni; if ((vstupni=fopen("seznam.txt","rt"))==NULL) { printf("Chyba: soubor seznam.txt nelze otevrit"); return -1; } while((c=fgetc(vstupni))!=EOF) { /* zpracování znaku c */ /* test na konec řádku */ if (c == '\n') ... } fclose(vstupni);
Program, který přečte textový soubor po znacích a vypíše obsah na obrazovku, je napsán podle této kostry.
Příklad:
Vstupní soubor: soubor1.txt CodeBlocks: soubor1.cbp, soubor1.c
int fprintf(FILE *stream, const char *format[, argument, ...]);Parametr
stream
je ukazatel na otevřený soubor, ostatní argumenty a používání funkce se neliší od funkce printf.
Funkce fprintf vrací počet znaků zapsaných do souboru. Pokud dojde k chybě, vrací funkce EOF.
Pro formátovaný vstup z textového souboru používáme funkci
int fscanf(FILE *stream, const char *format[, address, ...]);Parametr stream je ukazatel na otevřený soubor, ostatní argumenty a používání funkce se neliší od známé funkce scanf. Funkce provádí čtení (např. celého čísla), dokud nenarazí na bílý znak (mezera, tabulátor, nový řádek) nebo na znak nepatřící do zápisu konvertované hodnoty (např. nečíselný znak). Pokud načítáme řetězce, načte se text do prvního bílého znaku. Funkce vrací počet úspěšně načtených položek, pokud se nepodaří položku načíst (např. ve formátovacím řetězci je %d a v souboru je text) vrací 0, při pokusu číst na konci souboru vrací EOF.
Představme si textový soubor data.txt
, který obsahuje posloupnost celých čísel:
1 2 3 5 -4 10 20 5Fragment kódu, který přečte všechna čísla a spočítá součet, vypadá následovně:
FILE *fi; int s,x; fi = fopen("data.txt","rt"); if (fi != NULL) { s = 0; while(fscanf(fi,"%d",&x) != EOF) s += x; printf("Soucet je %d\n",s); fclose(fi); } else printf("Soubor nelze otevrit!\n");
V kódu není ošetřeno, zda se funkce fscanf
opravdu načetla ze souboru celé číslo, zde
se testuje pouze dosažení konce souboru. Kompletní kód je k dispozici:
Kód ke stažení:
Vstupní soubor: data.txt Dev C++: soucet.dev, soucet.c CodeBlocks: soucet.cbp, soucet.c
char *fgets(char *s, int n, FILE *stream);Funkce přečte do řetězce s jeden řádek z textového souboru. Parametr n je velikost řetězce a slouží jako omezovač, aby nedošlo k přeplnění pole. Jinak řečeno, funkce ukončí čtení dat, načte-li n-1 znaků ze souboru nebo narazí dříve na znak konce řádky. Funkce ponechá na konci řetězce znak konce řádky
'\n'
a připojí za něj znak konce řetězce '\0'. Při bezchybém čtení vrací ukazatel na řetězec
s, při chybě vrací NULL
.
Poznámka: Čteme-li soubor po řádcích a máme omezenou délku pole s, zjistíme snadno, zda jsme přečetli skutečně celý řádek - poslední znak řetězce musí být '\n' (výjimku tvoří poslední řádek souboru, který nemusí být ukončen znakem \n'.)
stdin, stdout, stderr
, funkce perror()
stdin
(standard input, standardní vstup), stdout
(standard output, standardní výstup) a stderr
(standard error,
standardní chybový výstup). Soubory se neotvírají pomocí fopen
, jsou již otevřené!
Původ souborů je třeba opět hledat v systému UNIX a u sálových počítačů. Standardní výstupem je konzole (terminál), standardní vstup
je také konzolový (klávesnice). Standardní chybový výstup byl u sálových počítačů směrován na tiskárnu, dnes je také posílán operačním systémem
na terminál (obrazovku). Funkce printf
vlastně posílá svůj výstup do souboru stdout a funkce scanf
čte tedy ze
souboru stdin. V programu bychom mohli tyto dvě funkce nahradit ekvivalentně voláním funkcí fprintf a fscanf, např.:
fprintf(stdout, "%d",a); fscanf(stdin,"%d",&a);Soubor
stdin
využijeme při čtení celého řádku z klávesnice. Místo funkce gets(char *s)
je lépe volat
funkci s parametrem standardního vstupu: fgets(s,n,stdin)
. Oproti funkci gets(char *s)
lze
předejít přetečení maximálního indexu při ukládání vstupního řetězce do pole.
Pro výstup chybových hlášení na standardní chybový výstup bychom měli používat funkci
void perror(const char *s);Funkce vytiskne chybové hlášení s a automaticky číslo chyby, uložené v globální proměnné
errno
,
resp. navíc systémové chybové hlášení. Proměnnou errno
nastavují některé
systémové funkce (viz dokumentace).
Tisk chybového hlášení bez vedlejších efektů lze provést také pomocí fprintf(stderr,"Chyba");
Příklad:
Dev C++: chyba.dev, chyba.c CodeBlocks: chyba.cbp, chyba.c
size_t fwrite(void *ptr, size_t size, size_t n, FILE *stream); size_t fread(const void *ptr, size_t size, size_t n, FILE *stream);Ukazatel
ptr
je ukazatel na blok paměti (pole), z/do kterého se data čtou/zapisují. Hodnota size je velikost
jedné položky (v bytech), n je počet položek, stream je ukazatel na otevřený soubor.
Funkce zapíší/přečtou n*size slabik do/ze souboru. Funce fwrite vrací počet skutečně zapsaných slabik (bytů) do souboru, funkce fread vrací počet skutečně přečtených slabik ze souboru.
Posouvat aktuální pozici v souboru („ukazovátko“) lze pomocí funkce
int fseek(FILE *stream, long offset, int whence);Parametr offset určuje, o kolik slabik (bytů) se má aktuální pozice v souboru posunout. Pozice je relativní nebo absolutní, podle parametru whence. Může být i záporná. Možné hodnoty parametru whence ukazuje následující tabulka:
Symbolická konstanta | Hodnota | Význam |
---|---|---|
SEEK_SET | 0 | Pozice od počátku souboru, hodnota offset je kladná |
SEEK_CUR | 1 | Relativní pozice vzhledem k aktuální, hodnota offset může být kladná i záporná |
SEEK_END | 2 | Pozice od konce souboru, hodnota offset je záporná |
S binárním souborem pracují i funkce pro zápis dat do textového souboru a naopak.
Napište program, který zapíše do binárního souboru celočíselné pole a poté obah opět přečte do jiného pole. Pro posuv na počátek použijte funkci fseek. Prohlédněte si obsah zapsaného souboru.Návod: Zápis poleŘešení:int c[10];
do binárního souborufo
se provádífwrite((void*)c,sizeof(int),10,fo);
Dev C++: zapispole.dev, zapispole.c CodeBlocks: zapispole.cbp, zapispole.c
CR LF
(Carriage Return, návrat vozíku, kód ASCII 13 dekadicky a Line Feed,
nový řádek, kód ASCII 10 dekadicky), v operačních systémech UNIXového typu je na konci řádku pouze znak LF, v systému Mackintosh
pouze znak CR. Při otevření souboru v textovém módu je znak konce řádku správně intepretován, tj. např. program s voláním funkce
putchar('\n');
(kde '\n' je znak LF) je přeložen v překladači pod systémy firmy Microsoft tak, aby zapisoval na konec řádku správně oba znaky,
tedy i CR. Program přeložený pod OS typu UNIX zapisuje pouze znak LF atd. Rovněž při čtení konce řádku je v systémech firmy Microsoft
„přeskočen“ znak CR.
Otevřeme-li textový soubor pod systémem firmy Microsoft jako binární, nedojde při čtení k této konverzi a načtou se oba znaky CR a LF po sobě,
tj. fgetc vrátí nejprve znak '\r' (CR) a při dalším volání '\n' (LF). Při zápisu do textového souboru, který by byl otevřen jako binární, bychom museli
zapsat oba znaky, tj. fputc('\r'); fputc('\n');
, aby byl textový soubor vytvořen správně. V systému UNIXového typu není rozdíl mezi prací s binárním
a textovým souborem.
[Přednáška 5] | [Obsah] | [Přednáška 7] |