IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

3. diel - Prúdy v C ++

V minulej lekcii, Typy súborov a správne umiestnenie súborov v C ++ , sme si povedali základné informácie pre prácu so súbormi rôznych formátov na rôznych operačných systémoch. Dnes si konečne vysvetlíme, čo to sú prúdy a na čo sú nám dobré.

Čo je to prúd

Prúd je sekvencia dát, ktoré lineárne spracovávame. Synonymom prúdu by mohol byť napríklad tok dát. Zoberme si výpis do konzoly. O tom sme si povedali, že je prúd. A kde je to "lineárna spracovania"? Dáta sa vypisujú tak, ako ich zapisujeme. Znak po znaku pridávame znaky do prúdu, ktorý "odteká" do konzoly. Rovnako je tomu pri objekte cin. Tam pre zmenu dátumu "pritekajú" z konzoly a my ich čítame. Napríklad ak chceme prečítať číslo, vyberieme z prúdu znaky reprezentujúce číslicu. Tieto znaky následne spracujeme (teda naparsujeme) a výsledkom je číslo.

Aké by boli ďalšie príklady prúdov? Napríklad, ak pracujeme s mikrofónom. Z okolia budeme nahrávať zvuk, ktorý je opäť iba prúd dát, ktorý naše aplikácie spracováva. Ďalej napríklad video. Tu nám dokonca figurujú dva prúdy. Jedným prúdom čítame dáta zo súboru, ktorá aplikácia následne spracováva. Druhým prúdom spracované dáta "vykresľuje" na obrazovku ako obrázky, a tým získame video.

Zjednotenie práce s dátami

Myšlienka prúdov (v anglickej literatúre streams) je veľmi silná. V aplikácii nás nemusí zaujímať, odkiaľ dáta pochádza. Napríklad v prípade videa môžeme dáta čítať zo súboru, zo siete, z kamery alebo dokonca môžeme snímať obrazovku a posielať dáta späť. Jadro aplikácie sa v takom prípade vôbec nemení, pretože vďaka prúdom je prístup k dátam jednotný.

Typy prúdov

Keď už vieme, čo to prúdy sú, môžeme si je teraz lepšie rozdeliť. Na začiatku sme si povedali, že dáta môžu "pritekať" a tiež "odtekať". Tým sú prúdy rozdelené na dve základné kategórie - vstupné a výstupné.

Vstupné prúdy dedí z triedy istream, ktorú nájdeme v hlavičkovom súbore istream. Trieda istream poskytuje rozhranie (API), ktoré sme videli a používali v prvej lekcii.

Výstupné prúdy dedí z triedy ostream, ktorá je definovaná v hlavičkovom súbore ostream. Jej použitie sme videli nielen v prvej lekcii, ale vo všetkých tutoriáloch, pretože práve objekt cout dedí z triedy ostream.

Niektoré prúdy môžu byť samozrejme oboje (teda ako vstupné tak výstupné). Takým príkladom je napríklad trieda fstream. Môžeme súbor otvoriť pre čítanie aj zápis, nejaké časti prečítať a nejaké časti prepísať. Musíte však počítať s tým, že pri zápise sa budú dáta prepisovať.

Seekable a non-seekable prúdy

Prúdy ďalej delíme na tzv. Seekable streams (voľne preložené asi ako vyhľadateľné prúdy) a non-seekable streams (neprohledatelné či čisto lineárny prúdy). Pretože Čeština nemá skutočný preklad týchto termínov, budem aj ďalej používať ich anglické názvy.

Seekable prúdy

Seekable prúdy sú prúdy, v ktorých sa môžeme presúvať. Napríklad keď máme dáta v pamäti a vieme, že sme na offsetu 64, je pre nás jednoduché sa presunúť na index 32 (iba presunieme ukazovateľ). Rovnako tak, ak čítame 100. bajt súboru a zrazu chceme prečítať 50. bajt, môžeme sa v súbore presunúť iba tým, že povieme operačnému systému, aby nám vrátil 50. bajt súboru. Seekable prúdy sú spravidla také, kde sú dáta už niekde zaznamenaná. Ak sa v takýchto dátach niekam presunieme, nestrácame žiadne informácie, pretože tie sú uložené niekde inde.

Non-seekable prúdy

Na druhej strane sú non-seekable prúdy, ktoré môžeme čítať (resp. Zapisovať) iba od začiatku do konca. Ak niektoré dáta preskočíme (napríklad použitím metódy ignore()), sa potom tieto dáta nenávratne stratená a už sa k nim nedostaneme. Rovnako tak sa nedostaneme k už prečítaným dátam, pretože tá sú už spracované. Typickým príkladom je práve konzola. Akonáhle na výstup niečo vypíšeme, nemožno (štandardnými prostriedkami) vypísaný text zmazať. Operačný systém samozrejme tieto prostriedky má, ale musíte použiť systémové volania a v tejto chvíli iba program využíva fakt, že si operačný systém výstup pamätá. Staršie zariadenia, ktoré tieto vlastnosti nemala, už text upraviť nedokázala. Ďalším príkladom non-seekable prúdu je na príklad počúvanie mikrofónu alebo čítanie dát zo siete. Akonáhle sú dáta spracované a vyrovnávacia pamäť prúdu je vymazaná, nie je už možné získať.

Teraz sa vrátime k seekable prúdom. Vzhľadom k tomu, že prúdy môžu byť aj vstupné aj výstupné, nevystačíme si iba s metódou seek(), ale musíme rozlíšiť pozíciu pre čítanie a pozíciu pre zápis. Metódy pracujúce s pozíciou pre zápis sú ukončené písmenom p (od slova put) a metódy pracujúce s pozíciou pre čítanie sú ukončené písmenom g (od slova get). Metódy máme k dispozícii nasledujúce:

  • tellg() - vráti aktuálnu pozíciu pre čítanie od začiatku prúdu (teda absolútna pozíciu)
  • tellp() - vráti aktuálnu pozíciu pre zápis od začiatku prúdu (teda absolútna pozíciu)
  • seekg( pos_type pos ) - nastaví pozíciu pre čítanie absolútne (tj. od začiatku prúdu); volaním proud.seekg(proud.tellg()) zostane pozície nezmenená
  • seekg( off_type off, std::ios_base::seekdir dir) - nastaví pozíciu pre čítanie relatívne, a to vzhľadom k použitému Flag, ktorý sa odovzdáva ako druhý parameter
  • seekp( pos_type pos ) - nastaví pozíciu pre zápis absolútne (tj. od začiatku prúdu)
  • seekp( off_type off, std::ios_base::seekdir dir) - nastaví pozíciu pre zápis relatívne, a to vzhľadom k použitému Flag, ktorý sa odovzdáva ako druhý parameter

Hodnotou druhého parametru metód seekx() môže byť:

  • ios:beg - od začiatku prúdu (teda rovnaký prípad ako predchádzajúci preťaženie)
  • ios:end - od konca prúdu (pozície musia byť záporná alebo 0)
  • ios:cur - od aktuálnej pozície

Poďme si to teraz vyskúšať na súboru:

fstream soubor("zapis.txt");

//precteni slova
string slovo;
char radek[128];
soubor >> slovo;
cout << "Po precteni slova \"" << slovo << "\" je pozice " << soubor.tellg() << " pro cteni a " << soubor.tellp() << " pro zapis" << endl;

//prepsani souboru
soubor.seekp(0);
soubor << "Ya Ya";
soubor.seekg(0);
soubor.getline(radek, 128);
cout << "Prvni radek souboru: " << radek << endl;

//zapis na konec
soubor.seekp(-1, ios::end);
soubor << " You are so awesome";
soubor.seekg(0, ios::beg);
soubor.getline(radek, 128);
cout << "Prvni radek po zmene souboru: " << radek << endl;

Štandardné prúdy

Už sme sa dozvedeli o objektoch cout a cin, ktoré slúžia ako prúdy pre vstup a výstup z konzoly.

Istream, ostreae a iostream

Ďalej vieme o triedach istream a ostream, ktoré slúžia ako bázovej triedy pre ostatné prúdy. istream poskytuje rozhranie pre vstupné prúdy, zatiaľ čo ostream poskytuje rozhranie pre prúdy výstupné. Ďalej existuje trieda iostream, ktorá (ako je z názvu zrejmé) poskytuje rozhranie pre vstupné i výstupné prúdy. Reálne trieda iostream dedí z tried istream a ostream.

Ifstream, ofstream a fstream

Pre prácu so súbormi sme použili triedu fstream, ktorá dedí z iostream a poskytuje tak obe rozhrania. Toto rozhranie môžeme však rozložiť a tak existujú v štandarde ďalej triedy ifstream (vstupný prúd pre súbory) a ofstream (výstupný prúd pre súbory). V tomto prípade už neplatí, že trieda fstream dedia z tried ifstream a ofstream, ale iba z triedy iostream.

Istringstream, ostringstream, stringstream

Standard ďalej definuje triedu stringstream v knižnici sstream, ktorá ukladá dáta v operačnej pamäti RAM. Tento prúd môžeme použiť pre odovzdávanie dát vnútri aplikácie. V súvislosti s použitím čisto RAM pamäte je potreba dať pozor na jeho veľkosť. Ak čítame súbor pomocou triedy fstream, súbor je čítaný po častiach a nenačíta sa do operačnej pamäte. Ak načítame veľký súbor (napríklad 4GB) a uložíme jeho obsah do objektu triedy stringstream, program zaberie minimálne 4GB operačnej pamäte.

Rovnako ako trieda fstream má aj trieda stringstream varianty iba pre čítanie (istringstream) alebo len pre zápis (ostringstream). Dáta pre istringstream naplníme pri jeho vytváraní. Triedu ostringstream prirodzene plniť nemusíme, pretože do nej ešte len dáta budeme odovzdávať. Dáta potom získame metódou str(), ktorá vráti obsah ako string. Ak zavoláme metódu str() s parametrom typu string, je vnútorný obsah prúdu nahradený obsahom parametra. To možno využiť i pre istringstream - ak je prúd prázdny, dodáme ďalšie dáta a zvyšok aplikácia môže s prúdom ďalej pracovať.

Hlavné použitie stringstream triedy je pre prevod objektov na reťazec. C ++ nemá nič ako metódu toString(). Pre výstup sa používa operátor <<, ako sme to robili u našich tried. Pomocou stringstream môžeme previesť objekt na reťazec rovnako, ako by sme objekt vypisovali:

class MojeTrida{};
ostream& operator<<(ostream& str, const MojeTrida& trida)
{
    return str << "Reprezentace tridy";
}


MojeTrida a;
ostringstream str;
str << a;
string prevedenoNaRetezec = str.str();
cout << "Vypis tridy jako retezec: " << prevedenoNaRetezec << endl;

Zhrnutie

Výnimočne si dovolím krátke zhrnutie dnešnej lekcie.

  • Prúd či stream je sekvencia bajtov, z ktorej čítame alebo do ktorej zapisujeme.
  • Prúdy delíme na vstupnú (Dedič z triedy istream) a výstupné (dedičov z triedy ostream).
  • Prúdy môžu byť seekable a non-seekable.
  • stringstream slúži ako náhrada toString() metódy.

Nakoniec prikladám kompletnú hierarchiu spomenutých tried v C ++.

Hierarchie prúdov v štandardnej knižnici C ++ - Súbory a práca s nimi v C ++

To je pre dnešné lekciu všetko. V tej nasledujúcej, Súborové prúdy v C ++ a UTF kódovanie , sa ešte raz pozrieme na súborové prúdy a naučíme sa pracovať s rozšírenou znakovou sadou.


 

Predchádzajúci článok
Typy súborov a správne umiestnenie súborov v C ++
Všetky články v sekcii
Súbory a práca s nimi v C ++
Preskočiť článok
(neodporúčame)
Súborové prúdy v C ++ a UTF kódovanie
Článok pre vás napísal Patrik Valkovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity