LINUXSOFT.cz Přeskoč levou lištu

ARCHIV



   

> 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 66904×

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ň.

modesoubor existovalsoubor neexistoval
rbotevře pro čteníchyba
rb+otevře pro čtení a zápischyba
wbsmaže obsah a otevře pro zápisvytvoří a otevře pro zápis
wb+smaže obsah a otevře pro čtení a zápisvytvoří a otevře pro čtení a zápis
abotevře pro zápis na konec souboruvytvoří a otevře pro zápis
ab+otevře pro čtení a zápis na konec souboruvytvoří 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

pridej.cz

 

DISKUZE

svele, jen tak dal 15.9.2005 11:15 P.B.




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 ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2024) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze