C/C++ (24) - Soubory
Dnes probereme operace se soubory. Ukážeme si, jak naprogramovat zjednodušené vlastní verze příkazů cat a cp.
15.9.2005 06:00 |
Jan Němec
| Články autora
| přečteno 67027×
Soubory
Funkce pro základní operace se soubory nalezneme ve standardní knihovně jazyka C.
Podobně jako v případě terminálového vstupu a výstupu musíme inkludovat
stdio.h. C rozlišuje soubory textové a binární (obecné) a pro oba
typy nabízí odlišnou sadu funkcí. Ve skutečnosti je z hlediska každého
normálního operačního systému obsah běžného souboru jen N-tice bytů a to, zda
se jedná o soubor textový nebo binární, je jen vlastností režimu práce s ním.
V textovém režimu se díváme na jeho obsah jako na textová data, s tím,
že některé znaky nebo jejich sekvence mají speciální význam, jde například
o odřádkování, tabelátor nebo znak konce souboru. Konkrétní sada a interpretace
těchto sekvencí
pak závisí na operačním systému, proto například unixový textový soubor vypadá
po otevření v notepadu ve Windows jako jedna dlouhá řádka s vloženými
nezobrazitelnými znaky v místech původních konců řádek.
Soubor, který obsahuje pouze textová data, je zpravidla výhodnější zpracovávat
v C v textovém režimu, ve výjimečných případech (například právě pokud chceme na
Linuxu generovat soubor čitelný v notepadu) však použijeme i na textové soubory
binární režim. Naproti tomu na soubor s jiným než textovým obsahem použít
textový režim sice také můžeme, ale rozumný smysl to nemá.
Textový režim
Nejjednodušším smysluplným příkladem je výpis textového souboru na standardní
výstup. Následující program je ekvivalentem příkazu
cat /etc/passwd
#include <stdio.h>
int main(void) {
FILE *fr;
char s[1024];
fr = fopen("/etc/passwd", "r");
if (!fr) {
fputs("Nemohu otevřít vstupní soubor.\n", stderr);
return 1;
}
while (fgets(s, sizeof(s), fr) != NULL) {
fputs(s, stdout);
}
fclose(fr);
return 0;
}
Pro soubory se ve standardní knihovně používají proměnné typu ukazatel na FILE, což
je strukturovaný typ, ale jeho konkrétní položky nás nemusejí zajímat,
slouží pro vnitřní potřebu runtime podpory jazyka C, programátor pracuje pouze
s ukazatelem na celou strukturu. První operace je otevření souboru /etc/passwd.
Funkce fopen vrací ukazatel na FILE - otevřený soubor, prvním parametrem je
jeho jméno a druhým režim práce, v našem případě "r" znamená textový soubor
otevřený pro čtení. Otevření souboru se nemusí podařit, proto je třeba otestovat
návratovou hodnotu. Funkci fgets už známe z dílu o standardním vstupu, jedná
se o čtení ze souboru fr řádky maximální délky sizeof(s) do bufferu s. Pokud
je řádka delší, funkce přečte jen její začátek a případné další čtení ze
souboru pomocí funkce fgets začne na místě, kde původní volání skončilo. I zde
návratová hodnota NULL znamená neúspěch, například konec souboru. Funkce fputs
je obdoba puts, pouze se píše do souboru. V našem případě je ovšem oním
souborem stdout, tedy standardní výstup. Podobně můžeme používat i proměnné stdin a stderr se zřejmým významem. Rozdíl mezi puts(s) a fputs(s, stdout)
je pouze v tom, že puts ještě navíc kromě výpisu řetězce odřádkuje, což se nám
teď nehodí, neboť řetězec s již obsahuje znak nového řádku. Po ukončení
práce se souborem je třeba jej zavřít funkcí fclose. Důležité je to především
v případě zápisu, neboť teprve fclose vyprázdní buffery standardní C knihovny
a fyzicky data uloží. Po čtení ze souboru voláme fclose "jen" z důvodu šetření
systémových prostředků a programátorské slušnosti.
Funkcí pro práci se soubory v textovém režimu je více a většinou jim (názvem
i chováním) odpovídá nějaké funkce pro práci se standardním vstupem a výstupem,
kterou jsme si již popsali v počátečních dílech našeho seriálu.
FILE *fopen(const char *path, const char *mode);
Výjimkou je
funkce fopen. V příkladu jsme si ukázali otevření /etc/passwd pro čtení
příkazem
fopen("/etc/passwd", "r");
V případě zápisu do textového souboru použijeme jako druhý parametr "w" nebo
"a". Pokud soubor neexistuje, v obou případech je vytvořen nový. Rozdíl nastane
u existujícího souboru. Otevření pomocí "w" smaže původní obsah souboru
(například textový editor ukládá soubor),
zatímco "a" znamená zápis na konec souboru (například přidání nového uživatele
do /etc/passwd). Funkce fopen nemusí vždy uspět, nejčastější příčinou je
nedostatečné oprávnění uživatele k danému souboru nebo adresáři.
Pro zápis můžeme použít uvedené analogie funkcí puts, putchar a printf. Hlavním
rozdílem je, že fputs sama od sebe neodřádkuje, jinak se všechny tři funkce
chovají podobně jako jejich protějšky pro standardní výstup.
int fputs(const char *s, FILE *stream);
int fputc(int c, FILE *stream);
int fprintf(FILE *stream, const char *format, ...);
Podobné je to i u čtení, i zde máme analogie funkcí gets, getchar a scanf.
Funkce gets má navíc parametr size, který omezí maximální délku přečtené řádky
(narozdíl od gets, která v případě nekonečné řádky čte tak dlouho, až program
spadne na přetečení bufferu a nelze tomu nijak zabránit), a pokud narazí na
konec řádky, uloží do bufferu i znak '\n', zatímco gets jej zahodí.
int fgets(char *s, int size, FILE *stream);
int fgetc(FILE *stream);
int fscanf(FILE *stream, const char *format, ...);
Po skončení čtení nebo zápisu soubor uzavřeme funkcí fclose.
int fclose(FILE *stream);
Binární režim
Pro práci se souborem, chápaným jako pole bytů, použijeme binární režim.
Následující příklad zhruba odpovídá příkazu
cp /etc/passwd /home/honza/passwd
Všimněte si, že použijeme binární mód pro kopírování souboru obsahující textová
data.
#include <stdio.h>
int main(void) {
FILE *fr, *fw;
unsigned char buf[1024];
size_t precteno, zapsano;
fr = fopen("/etc/passwd", "rb");
if (!fr) {
fputs("Nemohu otevřít vstupní soubor.\n", stderr);
return 1;
}
fw = fopen("/home/honza/passwd", "wb");
if (!fw) {
fclose(fr);
fputs("Nemohu otevřít výstupní soubor.\n", stderr);
return 2;
}
while (precteno = fread(buf, 1, sizeof(buf), fr)) {
zapsano = fwrite(buf, 1, precteno, fw);
if (precteno > zapsano) {
fputs("Chyba zápisu do souboru.\n", stderr);
break;
}
}
if (ferror(fr)) {
fputs("Chyba čtení ze souboru.\n", stderr);
}
fclose(fr);
fclose(fw);
return 0;
}
Nejprve jsme oba soubory otevřeli funkcí fopen. Písmeno 'b' v parametru
určujícím režim práce se souborem, znamená binární mód. Vlastní kopírování
probíhá ve while cyklu, který může skončit chybou čtení nebo zápisu a nebo
koncem souboru /etc/passwd. Funkce fread čte ze souboru fr do bufferu buf
sizeof(buf) bloků dat velikosti 1 byte. Návratovou hodnotou je počet přečtených
bloků, který je v našem případě shodou okolností rovný počtu přečtených bytů.
Pokud funkce narazí na konec souboru nebo dojde v průběhu čtení k chybě, vrátí
funkce menší počet bloků, případně nulu. Zápis do /home/honza/passwd probíhá
zcela analogicky funkcí fwrite. Zde je menší než požadovaný počet zapsaných
bloků dat vždy chybou. Po ukončení kopírovacího cyklu je dobré se ještě
ujistit, že cyklus neskončil kvůli chybě čtení ze souboru pomocí ferror,
neboť z návratové hodnoty fread nepoznáme, zda jsme jen narazili na konec
souboru nebo došlo k nějaké (například diskové) chybě.
Nyní si ukážeme nejdůležitější funkce pro binární režim.
FILE *fopen(const char *path, const char *mode);
Použití funkce fopen se od textového režimu liší především přidáním 'b' do
řetězce mode, navíc má rozumný smysl otevřít soubor pro čtení i zápis zároveň.
mode | soubor existoval | soubor neexistoval |
rb | otevře pro čtení | chyba |
rb+ | otevře pro čtení a zápis | chyba |
wb | smaže obsah a otevře pro zápis | vytvoří a otevře pro zápis |
wb+ | smaže obsah a otevře pro čtení a zápis | vytvoří a otevře pro čtení a zápis |
ab | otevře pro zápis na konec souboru | vytvoří a otevře pro zápis |
ab+ | otevře pro čtení a zápis na konec souboru | vytvoří a otevře pro čtení a zápis |
V otevřeném souboru je jakýsi neviditelný ukazatel na aktuální pozici, čtení
a zápis jej posunou o velikost přenášených dat. Tento ukazatel můžeme také
posouvat explicitně, proto může mít smysl otevírat soubor například v režimu
"ab+", ačkoli čtení z konce souboru smysl nemá.
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
Funkce fread a fwrite jsme si dostatečně vysvětlili již v příkladu, pouze
uvedu, že velikost přenášených dat je size * nmemb. Řada programátorů
to považuje za zbytečné zadávání jednoho čísla pomocí dvou parametrů a jako
parametr size předávají vždy 1.
int fclose(FILE *stream);
Použití funkce fclose se od textového režimu nijak neliší.
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
Funkcí fseek posouváme aktuální pozici v souboru a pomocí ftell ji můžeme
zjistit. Parametr whence nabývá hodnot SEEK_SET, SEEK_CUR a SEEK_END podle
toho, zda offset znamená posunutí od začátku, aktuální pozice nebo
konce souboru, offset tedy může být i záporné číslo.
int ferror(FILE *stream);
int feof(FILE *stream);
Pomocí funkcí ferror a feof zjistíme, zda je aktuální pozice na konci souboru
respektive zda došlo k chybě. Tyto funkce se používají i v textovém režimu.
int remove(const char *pathname);
Trochu stranou od ostatních uvedených funkcí je remove, která smaže soubor
nebo prázdný adresář.
Všimněte si, že řadu užitečných funkcí standardní C knihovna nenabízí.
Jedná se zejména o práci s adresáři nebo atributy souboru. V tomto případě
obvykle programátor použije systémová volání charakteristická pro konkrétní
typ operačního systémy, multiplatformní programy nad nějakou všeobjímající
knihovnou typu Qt nebo WXwidgets pak volají funkce této knihovny.
Pokračování příště
V příštím dílu si ukážeme implementaci vlastních funkcí s proměnným počtem
parametrů.
Verze pro tisk
|
Příspívat do diskuze mohou pouze registrovaní uživatelé.
|
|

Vyhledávání software

Vyhledávání článků
28.11.2018 23:56 /František Kučera Prosincový sraz spolku OpenAlt se koná ve středu 5.12.2018 od 16:00 na adrese Zikova 1903/4, Praha 6. Tentokrát navštívíme organizaci CESNET. Na programu jsou dvě přednášky: Distribuované úložiště Ceph (Michal Strnad) a Plně šifrovaný disk na moderním systému (Ondřej Caletka). Následně se přesuneme do některé z nedalekých restaurací, kde budeme pokračovat v diskusi.
Komentářů: 1
12.11.2018 21:28 /Redakce Linuxsoft.cz 22. listopadu 2018 se koná v Praze na Karlově náměstí již pátý ročník konference s tématem Datová centra pro business, která nabídne odpovědi na aktuální a často řešené otázky: Jaké jsou aktuální trendy v oblasti datových center a jak je optimálně využít pro vlastní prospěch? Jak si zajistit odpovídající služby datových center? Podle jakých kritérií vybírat dodavatele služeb? Jak volit vhodné součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně datové centrum spravovat? Jak co nejlépe eliminovat možná rizika? apod. Příznivci LinuxSoftu mohou při registraci uplatnit kód LIN350, který jim přinese zvýhodněné vstupné s 50% slevou.
Přidat komentář
6.11.2018 2:04 /František Kučera Říjnový pražský sraz spolku OpenAlt se koná v listopadu – již tento čtvrtek – 8. 11. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma umění a technologie, IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář
4.10.2018 21:30 /Ondřej Čečák LinuxDays 2018 již tento víkend, registrace je otevřená.
Přidat komentář
18.9.2018 23:30 /František Kučera Zářijový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 20. 9. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář
9.9.2018 14:15 /Redakce Linuxsoft.cz 20.9.2018 proběhne v pražském Kongresovém centru Vavruška konference Mobilní řešení pro business.
Návštěvníci si vyslechnou mimo jiné přednášky na témata: Nejdůležitější aktuální trendy v oblasti mobilních technologií, správa a zabezpečení mobilních zařízení ve firmách, jak mobilně přistupovat k informačnímu systému firmy, kdy se vyplatí používat odolná mobilní zařízení nebo jak zabezpečit mobilní komunikaci.
Přidat komentář
12.8.2018 16:58 /František Kučera Srpnový pražský sraz spolku OpenAlt se koná ve čtvrtek – 16. 8. 2018 od 19:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát jsou tématem srazu databáze prezentaci svého projektu si pro nás připravil Standa Dzik. Dále bude prostor, abychom probrali nápady na využití IoT a sítě The Things Network, případně další témata.
Přidat komentář
16.7.2018 1:05 /František Kučera Červencový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 7. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát bude přednáška na téma: automatizační nástroj Ansible, kterou si připravil Martin Vicián.
Přidat komentář
Více ...
Přidat zprávičku
 Poslední diskuze
31.7.2023 14:13 /
Linda Graham iPhone Services
30.11.2022 9:32 /
Kyle McDermott Hosting download unavailable
13.12.2018 10:57 /
Jan Mareš Re: zavináč
2.12.2018 23:56 /
František Kučera Sraz
5.10.2018 17:12 /
Jakub Kuljovsky Re: Jaký kurz a software by jste doporučili pro začínajcího kodéra?
Více ...
|