Geek tričko zdarma Geek tričko zdarma
Hledáme grafika na pohodovou brigádu v Blenderu nebo programátora na hry v PyGame. Máš zájem? Napiš nám na redakce [zavináč] itnetwork.cz!
Tričko zdarma! Stačí před dobitím bodů použít kód TRIKO15. Více informací zde

Lekce 1 - Úvod do práce se soubory v jazyce C++

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

Vítejte u první lekce kurzu tutoriálů o práci se soubory a proudy v jazyce C++.

K čemu potřebujeme soubory?

Dosud jsme vždy pracovali pouze se standardním vstupem a výstupem (tedy s objekty cout a cin). Veškerá data jsme měli uložená v paměti RAM. To má nespornou výhodu v rychlosti jejich čtení, ovšem po ukončení aplikace jsou všechna data z paměti smazána a tím ztracena. Pokud naši aplikaci spustíme znovu, k datům se již nedostaneme (protože již ani neexistují). Zde přichází na řadu soubory. Do souborů si můžeme vložit libovolnou informaci a při příštím spuštěním aplikace ji opět načíst.

V C++ je práce se soubory součástí většího celku, který se nazývá proudy. Co to jsou proudy a jak s nimi pracovat si povíme později. V této lekci se podíváme na základní práci se soubory.

Základní operace

Pro práci se soubory má C++ dvě API. Jedno API je v hlavičce cstdio a jedná se o API zděděné z jazyka C. Toto API je popsáno v kurzu Práce se soubory v jazyce C a nebudeme se jím nadále zabývat. Čistě C++ API pro práci se soubory nabízí knihovna fstream. V ní je (kromě jiných tříd) třída fstream, se kterou budeme dnes pracovat.

API je zkratka pro Application Programming Interface. To se dá přeložit jako "rozhraní pro programování aplikací" a jedná se o souhrn funkcí a tříd, které může programátor využívat. Například napíšete-li knihovnu pro sečtení dvou čísel, její rozhraní bude např. funkce double secti(double a, double b). Každá knihovna poskytuje nějaké rozhraní, v opačném případě by nebyl způsob jak s ní pracovat. To, že můžeme vypisovat do konzole jako cout << neco, je opět pouze API, které poskytuje objekt cout (nebo obecněji standardní knihovna).

Otevření souboru pro zápis

Soubor nejdříve musíme otevřít (či vytvořit, pokud neexistuje). To provádíme konstruktorem objektu, který přijímá název souboru jako parametr. Pokud chceme soubor i vytvořit, předáme jako druhý parametr konstruktoru hodnotu ios::out. Poté ze souboru můžeme číst nebo do něj zapisovat. Následuje uvolnění prostředků pomocí metody close().

Ukažme si první příklad, který otevře soubor pro zápis:

fstream soubor("zapis.txt", ios::out);

// operace se souborem

//nemusi byt volano, bude zavolano po destruovani objektu
soubor.close();

Pokud bychom objekt neuzavřeli, soubor zůstane v držení aplikace. V takovém případě vám operační systém neumožní se souborem manipulovat (například jej smazat), dokud není soubor uvolněn. Jedná se o klasickou hlášku "Soubor nelze smazat, protože je používán jiným procesem". Nutno podotknout, že třída fstream provádí uzavření automaticky v destruktoru, metodu close() je tedy nutné volat jen pro objekty, jejichž platnost je delší než je jejich použití (například na začátku metody main()).

Zápis do souboru

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Umíme otevřít již existující soubor pro zápis. A jak že do něj vlastně zapisovat? Překvapivě stejně, jako jsme to dělali s objektem cout. Objekt cout je totiž podobně jako třída fstream pouze proud. Pojďme tedy vypsat do souboru text "Hello World":

fstream soubor("zapis.txt", ios::out);

soubor << "Hello World" << endl;

//nemusi byt volano, bude zavolano po destruovani objektu
soubor.close();

A pokud chceme se souboru něco přečíst? Opět použijeme to, co už známe. Čtení probíhá stejně jako u objektu cin, který je (překvapivě) opět pouze proud. Načtěme text, který jsme do souboru zapsali výše:

fstream soubor("zapis.txt");

string slovo;
soubor >> slovo;
cout << slovo << endl;

//nemusi byt volano, bude zavolano po destruovani objektu
soubor.close();

Nyní již umíme základní operace. To ale není vše. Proudy toho umí mnohem více a teď si představíme jejich základní metody.

Neformátovaný vstup a výstup

Vše, co jsme zatím prováděli, jsme prováděli na tzv. formátovaném vstupu, resp. výstupu. Proč mluvíme o formátovaném? Pokud jsme chtěli vypsat například číslo, pouze jsme do cout poslali int a cout se už postaral o správný převod čísla na text. Jinými slovy námi předaná data zformátoval do čitelné podoby. Někdy kvůli výkonu chceme formátování přeskočit a vypsat pouze text. O to se stará právě neformátovaný vstup, resp. výstup.

Neformátovaný výstup

Pro výstup jsou to dvě metody, put() a write(). Ta první předá na výstup pouze jeden znak, zatímco metoda write() vypíše pole. Proč bychom chtěli používat neformátovaný výstup? Protože je rychlejší než výstup formátovaný. Pro neformátovaný výstup neprobíhá žádná konverze, žádná kontrola, obsah pole je pouze zkopírován.

Neformátovaný vstup

Pro neformátovaný vstup je metod více. Základem je metoda get(), která ze vstupu odebere jeden znak. Existuje i přetížení, které ze vstupu odebere znaků několik.

  • int get() vrátí znak ze vstupu.
  • istream& get (char& c) vrátí znak ze vstupu argumentem.
  • istream& get (char* s, streamsize n) vrátí až n-1 znaků ze vstupu a uloží je do pole s. Funkce skončí po přečtení n-1 znaků nebo po výskytu znaku \n na vstupu (ten ve výstupu zůstává). Na konec pole je automaticky přidán znak \0 a jedná se tedy o plnohodnotný C-like řetězec (proto čte max n-1 znaků).
  • istream& get (char* s, streamsize n, char delim) stejný jako předchozí případ, pouze lze namísto ukončovacího symbolu \n zvolit cokoliv jiného.
  • istream& get (streambuf& sb) přečte vstup až po znak \n a vloží jej do streambufferu (viz. další lekce).
  • istream& get (streambuf& sb, char delim) přečte vstup až po znak delim a vloží jej do streambuferu (viz. další lekce).

Vstup má dále metody read() a getline(), které fungují téměř stejně jako třetí případ metody get(). Metoda read() pouze přečte n znaků nebo selže (nepřidává \0 znak). Metoda getline() přečte maximálně n-1 znaků, přidá \0 symbol a ukončovací symbol delim odstraní ze vstupu, pokud na něj narazila. K tomu, jak vstup může selhat, se dostaneme někdy jindy.

Nahlížení

Kromě samotného čtení můžete na znaky pouze nahlížet, a to metodou peek(). Ta vrátí další znak ze vstupu, ale neodstraní jej (takže zůstane pro další čtení k dispozici).

Přidávání

Na výstup můžete dokonce i znaky přidávat, a to metodou putback(). Ta vloží jeden znak do fronty pro čtení. To se samozřejmě neprojeví v konzoli (text, který uživatel napsal, tam zůstane), ale při dalším čtení bude znak přečten.

Ignorování

Nakonec tu máme metody pro ignorování vstupu, tj. metodu ignore(). Ta je deklarovaná jako istream& ignore (streamsize n = 1, int delim = EOF). Parametr n udává počet znaků k odstranění (-1 znamená bez limitu) a delim definuje znak, kde se má metoda zastavit. Konstanta EOF označuje konec souboru (taktéž bude probráno dále).

Následující program demonstruje základní neformátované operace:

//{
fstream zapis("soubor.txt", ios::out);
zapis.write("Hello World", 11);
zapis.put('!');
zapis.put('?');
zapis.close();
//}

fstream cteni("soubor.txt", ios::in);
cout << "Dalsi znak: " << (char)cteni.peek() << endl;
char prvni = (char)cteni.get();
char druhy = (char)cteni.get();
cout << "Po znaku " << prvni << " je znak " << druhy << endl;
char zbytekHello[4];
cteni.get(zbytekHello, 4, ' ');
cout << "Zbytek slova je " << zbytekHello << endl;
cteni.ignore(999, 'l');
cout << "Dalsi znak po prvnim ignorovani: " << (char)cteni.peek() << endl;
cteni.ignore(1);
char zbytekRadku[3];
cteni.getline(zbytekRadku, 3);
cout << "Po ignorovani je zbytek radku " << zbytekRadku << endl;
cteni.close();

Všimněte si explicitního uzavření prvního souboru. Pokud by první objekt nebyl uzavřen, druhý by z něj nemohl číst, protože soubor může být otevřen vždy jen jednou. Nicméně pokud by byl první blok kódu uzavřen do složených závorek, potom se na jejich konci zavolá destruktor objektu zapis a tak můžeme volání metody close() vynechat.

Kompletní výklad, co druhý parametr konstruktoru ios::out dělá, si necháme na někdy jindy.

Tím máme základy za sebou. Příště, v lekci Typy souborů a správné umístění souborů v C++, se podíváme na různé typy souborů a jak s nimi nakládat.


 

 

Článek pro vás napsal Patrik Valkovič
Avatar
Jak se ti líbí článek?
1 hlasů
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Všechny články v sekci
Práce se soubory v C++
Miniatura
Následující článek
Typy souborů a správné umístění souborů v C++
Aktivity (2)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!