Ulazno-izlazna biblioteka

Standardna ulazno izlazna biblioteka brine o prijenosu podataka između programa, datoteka te standardnog ulaza (tastature) i standardnog izlaza (ekrana). Prijenos podataka je konceptualiziran kao tok podataka (eng. stream) između programa i vanjskog medija.

Ulazno izazni tipovi su definirani u tri datoteke zaglavlja:

<iostream>

<fstream>

<sstream>

Pojedini tipovi su u sljedećoj tabeli raspoređeni prema datotekama zaglavlja.

Zaglavlje Tipovi

<iostream>

istream čita iz streama

ostream piše u stream

iostream čita iz i piše u stream

<fstream>

ifstream čita iz datoteke

ofstream piše u datoteku

fstream čita iz i piše u datoteku

<sstream>

istringstream čita iz stringa

ostringstream piše u string

stringstream čita iz i piše u string

Standardni ulaz i izlaz

Za unos podataka koristimo istream objekt cin kojeg obično nazivamo standardni ulaz.

Za ispis podataka na standardni izlaz koristimo ostream objekt cout. Pored njega prisutna su još dva ostreama objekta, cerr i clog koji služe za ispis grešaka i logova (standardni izlazi za greške i logove).

Standardni ulazno-izlazni tokovi vezani su za terminal u kojem se program izvršava, no na većini operacijskih sustava mogu se preusmjeriti.

Primjer:

#include  <iostream>

int main()
{
    int v1, v2;
    std::cout <<  "Unesite dva broja:"<<std::endl;
    std::cin >> v1 >> v2;

    std::cout <<  "Suma brojeva " << v1 <<  " i " << v2
              <<  " je jednaka " << v1+v2 <<  "." << std::endl;

     return  0;
}

Ispis

Za ispis podataka na izlazni tok koristimo operator <<. Taj se operator u primjeru

std::cout <<  "Unesite dva broja:"<< std::endl;

koristi dva puta. Operator na lijevoj strani mora biti ostream objekt, dok je na desnoj strani operand koji se ispisuje.

To je ekvivalentno naredbama

    std::cout <<  "Unesite dva broja:";
    std::cout << std::endl;

endl je jedan od tzv. manipulatora i definiran je u imeniku std. Njegov efekt pri ispisu na izlazni tok je ispis znaka za prijelaz u novi red i slanje međuspremnika na izlaz (flushing) što osigurava trenutni ispis čitavog međuspremnika koji čuva podatke koji su poslani na izlazni tok ali još nisu ispisani.

Operator << vrši formatirano ispisivanje. U kojem formatu će podatak biti ispisan ovisi o tipu podatka koji se ispisuje.

Unos

Čitanje podataka s ulaznog toka vrši se pomoću operatora >>. Lijevi operand je ulazni tok, a desni je varijabla u koju se podatak upisuje. Operacije čitanja možemo ulančavati. Izraz

    std::cin >> v1 >> v2;

je ekvivalentan s

    std::cin >> v1;
    std::cin >> v2;

Operator >> služi za formatirano učitavanje. U kojem formatu će podatak biti učitan ovisi o tipu varijable u koju se učitavanje vrši.

Testiranje toka

Podatke sa standardnog ulaza možemo sekvencijalno čitati se dok ne naiđemo na kraj ulaz tako da ispitujemo stanje toka u while petlji ili if naredbi. U sljedećem primjeru sumiramo sve učitane brojeve, ne znajući unaprijed koliko će ih biti.

#include  <iostream>

int main()
{
     int v, suma= 0;
     while(std::cin >> v)
        suma += v;
    std::cout <<  "Suma = " << suma << std::endl;

     return  0;
}

Čitanje podatka sa standardnog ulaza dešava se u while petlji:

while(std::cin >> v)

Nakon što je podatak pročitan izraz vraća ulazni tok koji se testira u while naredbi. Prilikom testiranja toka vraća se njegovo stanje koje može biti ispravno ili neispravno. Stanje ulaznog toka je ispravno ako je moguće pročitati sljedeći podatak iz toka. U suprotnom je neispravno i tada će while petlja završiti.

Ulazni tok dolazi u neispravno stanje u slučaju pogreške ili nailaska na kraj toka (end-of-file marker). Stoga u ovakvom kodu ta dva događaja nije moguće razlikovati. U oba slučaja petlja će se zaustaviti.

Pri čitanju sa standardnug ulaza, da bismo zaustavili čitanje, trebamo unijeti end-of-file marker. Na Linux i Mac OS-X sustavima to je obično Enter i Ctrl-d, a pod Windowsima Enter i Ctrl-z.

Funkcije za čitanje - getline

Stringove možemo čitati sa standardnog ulaza kao i sve druge podatke pomoću operatora >>. Pri tome treba znati da se kao delimiter između različitih stringova koriste bjeline (koje pored bjelina uključuju tabulatore i znak za prijelaz u novi red). Dakle, pri upisu bit će preskočene prethodne bjeline i čitanje će biti zaustavljeno nailaskom na prvu bjelinu. To ilustrira sljedeći primjer:

#include  <iostream>
#include  <string>

 int main()
{
    std::string word;

     while(std::cin >> word)
        std::cout << word<< std::endl;
     return  0;
}

Ukoliko želimo pročitati čitavu liniju sa svim eventualnim bjelinama trebamo koristiti globalnu funkciju getline

istream& std::getline ( istream& is, string& str );

koja je deklarirana u zaglavlju <string>. Ona čita do znaka za prijelaz u novi red, koji pročita i odbaci. Vraća ulazni tok koji se može testirati kao u sljedećem primjeru u kojem učitavom ulaz liniju po liniju sve do kraja ulaznog toka.

#include  <iostream>
#include  <string>

 int main()
{
    std::string line;

     while(std::getline(std::cin, line))
        std::cout << line << std::endl;
     return  0;
}

Da li je učitavanje uspjelo saznajemo testiranjem ulaznog toka.

Napomena. std::getline() pročita znak za prijelaz u novi red ali ga ne dodaje u string.

Napomena. Ulazni tokovi imaju metodu članicu getline() istog imena i iste funkcionalnosti, ali ona radi sa C-stringovima (nizovima znakova završenim nul znakom).

Fukcija getline() ima i treći parametar koji ima predodređenu vrijednost. Taj parametar je separator i ako se ne zada ima vrijednost prijelaza u novi red (\n).

istream& std::getline ( istream& is, string& str, char separator );

Zadavanjem bjeline kao separatora moguće je ulazni tekst čitati riječ po riječ.

Funkcije za čitanje - get

U nekim situacijama zgodnije je čitanje vršiti znak po znak. Tome služi funkcija

std::istream & std::istream::get(char & c)

koja će učitati sljedeći znak u varijablu c te vratiti ulazni tok na kome možemo provjeriti je li upis uspio. Sljedeći primjer ilustrira takav način učitavanja:

#include  <iostream>
#include  <string>

using namespace std;

int main()
{
    char c{'\0'};
    string text;
    // Funkcija get će učitati svaki znak. Zaustavljanje
    // unosa se postiže sa enter i Ctrl-D (linux)
    // ili sa enter i Ctrl-Z (windows).
    while( cin.get(c)  )
        text.push_back(c);

    cout << text << endl;
    return  0;
}

Isti efekt se postiže ako na cin uklonimo zastavicu skipws koja određuje da se bjeline preskaču ako je postavljena, odnosno ne preskaču ako nije.

char c{'\0'};
std::string text;
cin.unsetf(ios::skipws); // ukloni zastavicu
while(cin >> c)
    text.push_back(c);
cin.setf(ios::skipws);   // vrati zastavicu
cout << text << endl;

Stanje streama

Kod učitavanja podatak greške se redovito dešavaju. IO biblioteka ima stoga niz metoda koje indiciraju u kakvom je stanju stream (ulazni kao i izlazni).

Stanje streama je određeno variablom tipa std::iostate definiranoj u klasi std::ios_base. Predefinirano je nekoliko konstanti tipa iostate koje su dane u sljedećoj tabeli:

Konstanta Značenje

std::ios_base::goodbit

Sve je uredu.

std::ios_base::eofbit

Došlo se do kraja datoteke (nađen end-of-file).

std::ios_base::failbit

Greška; operacija neuspješna.

std::ios_base::badbit

Fatalna greška.

Razlika između situacija koje signaliziraju failbit i badbit je sljedeća: badbit signalizira da je stream korumpiran i podaci su eventualno izgubljeni. Čitanje stoga nije moguće nastaviti. failbit, s druge strane, kaže da je zadnje čitanje neuspjelo no stream je u konzistentnom stanju i čitanje se može nastaviti.

eofbit se postavlja kada se pokuša čitati iza EOF znaka. Istovremeno s njim se postavlja i failbit.

Funkcije koje daju stanje streama

Biblioteka definira niz funkcija za manipuliranjem sa stanjem streama. Kada želimo saznati stanje neke zastavice trebamo koristiti te funkcije budući da one vraćaju true ili false, dok je tip zastavice određen sustavom.

Poziv funkcije Povratna vrijednost

s.eof()

true ako je eofbit postavljen na streamu.

s.fail()

true iako je failbit postavljen na streamu.

s.bad()

true ako je badbit postavljen na streamu.

s.good()

true ako je stream u ispravnom stanju.

s.clear()

Postavi sve vrijednosti stanja na "ispravno" na streamu s.

s.clear(flag)

Stavi specifičnu zastavicu stanja na vrijednost flag, a druge poništi. Tip varijable flag je ios_base::iostate.

s.setstate(flag)

Postavljanje dodatnih zastavica.

s.rdstate()

Vrati tekuće stanje streama kao varijablu tipa ios_base::iostate.

Primjer

int main(int argc, char ** argv)
{
   int ival;
   // čitaj cin i testiraj samo na EOF;
   while (!std::cin.eof()) {
       std::cin >> ival;     // Čitaj podatak
       std::cout << "ival = " << ival << std::endl;

       if (std::cin.bad())  // input stream korumpiran; izađi
          throw std::runtime_error("IO stream defektan" );

       if (std::cin.fail()) // loš ulaz
       {
            std::cin.clear(); // resetiraj stream
            std::string t;
           //čitaj podatak koji nije mogao biti konvertiran u int
            std::cin >> t;
            std::cout << "Pogresan podatak na ulazu (procitao "
                      << t << "). Pokusajte ponovo." << std::endl;
            continue; // idi na novi ulaz
       }
      // ok, procesiraj ival
      // ...
   }
   return (EXIT_SUCCESS );
}

Datoteke

Datoteka zaglavlja <fstream> definira tri tipa:

  • ifstream, koji proširuje istream i služi čitanju iz datoteke;

  • ofstream, koji proširuje ostream i služi pisanju u datoteku;

  • fstream, koji proširuje iostream i služi čitanju i pisanju u/iz iste datoteke.

Iz činjenice da su klase za rad s datotekama nastale proširenjem klasa za rad sa standardnim ulazom/izlazom proizlazi da one dijele isto sučelje, odnosno da formatirano pisanje i čitanje vršimo pomoću operatora << i >>, te da stanje streama ispitujemo na isti način.

Otvaranje streama

File stream možemo otvoriti tako da konstruktoru damo ime datoteke.

// Ime datoteke koju želimo čitati
std::string file_name("datoteka.txt" );

// Otvori ulazni stream prema datoteci "datoteka.txt"
std::ifstream infile(file_name);
// Otvori izlazni stream prema datoteci "output.txt"
std::ofstream outfile("output.txt" );

Nakon ovih operacija možemo koristiti stream infile za čitanje iz datoteke datoteka.txt, a stream outfile za pisanje u datoteku output.txt.

Druga mogućnostje iskoristiti defaultne konstruktore i metodu open:

// Otvori stream prema datoteci "datoteka.txt"
std::ifstream infile; // Defaultni konstruktor ifstream klase
infile.open(file_name);

std::ofstream outfile; // Defaultni konstruktor ofstream klase
outfile.open("output.txt" );

Oba načina povezivanja streama s datotekom su ekvivalentna.

Zatvaranje streama

Nakon otvaranja datoteke treba provjeriti je li operacija uspjela. To se čini ispitivanjem streama u if naredbi.

if (!infile)
{
   std::cerr << "Greška: ne mogu otvoriti ulaznu datoteku: "
             << file_name << std::endl;
   return EXIT_FAILURE ;
}

Nakon što smo koristili stream moramo ga zatvoriti metodom close. Jednom zatvoreni stream može se ponovo otvoriti pomoću open i povezati s nekom drugom datotekom.

     infile.close();                         // Zatvori ulazni stream.
     infile.clear();
     infile.open("neka_druga_datoteka");     // Ponovo otvori isti stream.

Prije ponovnog otvaranje streama treba pozvati metodu clear radi eliminacije grešaka koje su se eventualno pojavile u prethodnom procesiranju.

Primjer

Ovdje imamo potpun primjer čitanja cijelih brojeva iz jedne datoteke i njihovog prepisivanja u drugu.

#include  <iostream>
#include  <fstream>
#include  <string>

int main()
{
    std::string file_name("datoteka.txt" );
    std::ifstream infile;
    infile.open(file_name.c_str());
    if (!infile)
    {
        std::cerr << "Greška: ne mogu otvoriti ulaznu datoteku: "
                  << file_name << std::endl;
        return EXIT_FAILURE ;
    }

    std::ofstream outfile;
    outfile.open("output.txt" );

    if (!outfile)
    {
        std::cerr << "Greška: ne mogu otvoriti izlaznu datoteku: "
                  << "output.txt" << std::endl;
        return EXIT_FAILURE ;
    }

    int tmp;
    // Čitaj cijele brojeve is datoteke datotek.txt
    // i ispiši ih u datoteku output.txt
    while (infile >> tmp)
        outfile << tmp << std::endl;

    infile.close();
    outfile.close();
    return EXIT_SUCCESS ;
}

Načini otvaranja datoteke

Kada otvaramo neku datoteku za čitanje ili pisanje automatski se postavljaju neke zastavice u ovisnosti o tipu streama. Popis tih zastavica dan je u sljedećoj tabeli.

Zastavica Značenje

in

Otvori za čitanje

out

Otvori za pisanje

app

Prije svakog pisanja idi na kraj datoteka

ate

Idi na kraj datoteke odmah nakon otvaranja

trunc

"truncate" postojeći stream pri otvaranju

binary

Operacije vrši binarno

Nisu sve zastavice kompatibilne sa svakim streamom. Na primjer, in zastavica se može postaviti samo na ulaznom streamu, ali tamo je postavljena implicitno tako da ju ne moramo sami postavljati. Slično je i s out zastavicom. Korisna može biti app zastavica koju možemo postaviti na izlaznom streamu. Ako želimo pisati u datoteku koja nije prazna ona će biti prije prvog pisanja obrisana (trunc je implicitno postavljen). Ako želimo nadopisivati u datoteku treba postaviti app zastavicu kao u ovom primjeru:

std::ofstream out;
out.open( "A.txt" );
out <<  "Linija 1   \n" ;
out.close();
std::ofstream out1;
// Ako otvorimo bez zastavice app file će biti obrisan.
out1.open( "A.txt" , std::ofstream::app);
out1 <<  "Linija 2   \n" ;
out1.close();

Datoteku je moguće otvoriti i u binarnom modu i tada za ulaz i izlaz treba koristiti posebne funkcije. Korištenjem fstream-a moguće je datoteku otvoriti za istovremeno pisanje i čitanje.

Formatiranje ulaza/izlaza

Stanje streama i manipulatori

Ulazno-izlazni streamovi pamte određen broj parametara koji određuju način formatiranja ispisa, odnosno format ulaznih podataka.

Korisnik može mijenjati te unutarnje parametre streamova pomoću manipulatora. Manipulatori su objekti ili funkcije koji se mogu koristiti kao operandi ulaznog/izlaznog streama s ciljem da promjene njegovo stanje. Oni vraćaju stream objekt tako da se mogu ubacivati u niz čitanja i pisanja.

Jedan primjer manipulatora je endl. Kada ispiše u izlazni stream

std::cout << std::endl;

On ispiše znak za prijelaz u novi red i šalje međuspremnik streama na izlaz. Na sličan način funkcioniraju i drugi manipulatori. Oni se prividno ispisuju i učitavaju, dok ustvari vrše neke druge operacije na streamu.

Format logičkog tipa

bool varijabla se po defaultu ispisuje kao 0 ili 1. true i false dobivamo primjenom boolalpha manipulatora. To ilustrira ovaj program:

#include <iostream>
#include <cstdlib>

using namespace std;

int main ( int argc, char *argv[] )
{
 cout << "defaultni ispisbool vrijednosti: "
      << true << " " << false
      << " \n Nakon boolalpha: "
      << boolalpha
      << true << " " << false
      << endl;

 return EXIT_SUCCESS ;
}

Izlaz programa je ovaj:

defaultni ispis  bool vrijednosti: 1 0
Nakon boolalpha: true false

Nakon što smo "ispisali" boolalpha svako daljnje ispisivanje logičkih vrijednosti koristi true i false. To ponašanje poništavamo s noboolalpha:

     bool x = true;
     cout << "Ispis nakon boolalpha: x = " << x
          << noboolalpha << endl
          << "Ispis nakon noboolalpha: x = " << x << endl;

Nakon noboolalpha, true i false se ponovo ispisuju kao 1 i 0.

Baza cjelobrojnih tipova

Svi se cijeli brojevi ispisuju u bazi 10. To se mijenja pomoću manipulatora hex, oct i dec. hex daje heksadecimalni ispis, oct oktalni, a dec služi za povratak na decimalni.

const int val = 15;
cout << "defaultni ispis: val = " << val  << endl;
cout << "oktalno:         val = " << oct << val << endl;
cout << "heksadecimalno:  val = " << hex << val << endl;
cout << "decimalno:       val = " << dec << val << endl;

Ispis:

defaultni ispis: val = 15
oktalno:         val = 17
heksadecimalno:  val = f
decimalno:       val = 15

Promjena koju vrše ovi manipulatori je trajna.

Oznaka baze na izlazu

Općenito se ne ispisuje. To se mijenja s manipulatorima showbase i noshowbase. Pri tome u ispisu imamo sljedeće pravilo:

  • Vodeći 0x indicira heksadecimalan broj;

  • Vodeća 0 indicira oktalan broj;

  • Odsutnost oznake indicira decimalan broj.

const int val = 15;
cout << showbase;  // Prikaži bazu
cout << "defaultni ispis: val = " << val  << endl;
cout << "oktalno:         val = " << oct << val << endl;
cout << "heksadecimalno:  val = " << hex << val << endl;
cout << "decimalno:       val = " << dec << val << endl;
cout << noshowbase;  // Vrati staro stanje

Ispis:

defaultni ispis: val = 15
oktalno:         val = 017
heksadecimalno:  val = 0xf
decimalno:       val = 15

noshowbase manipulator poništava showbase.

Heksadecimalni brojevi se ispisuju s malim slovima. Želimo li velika slova koristimo uppercase/nouppercase:

     cout << uppercase << showbase << hex
         << "Hexadecimalno:   val = " << val << endl;

Ispis:

Hexadecimalno:   val = 0XF

nouppercase manipulator poništava akciju uppercase manipulatora.

Kontrola ispisa fp-brojeva

Sljedeći su aspekti formatiranja fp-brojeva:

  • Preciznost: broj ispisanih znamenaka;

  • Notacija: s eksponentom ili bez njega;

  • Decimalna točka kod cjelobrojnih vrijednosti.

Normalno se koristi 6 znamenki u ispisu. Ako je broj cijeli ne ispisuje se decimalna točka. Hoće li biti ispisan s eksponentom ili ne ovisi o veličini broja.

Promjena preciznosti

Preciznost ispisa (broj znamenki u ispisu) postavlja se pomoću funkcije precision ili pomoću setprecision manipulatora. Funkcija precision uzima nula ili jedan argument. Kad uzima argument onda postavlja preciznost, dok bez argumenta vraća trenutnu preciznost. Manipulator setprecision uzima jedan argument i postavlja preciznost.

Primjer:

#include <iomanip>
// cout.precision daje trenutnu preciznost
cout << "Preciznost:  "  << cout.precision()
     << ", Vrijednost: " << sqrt(2.0) << endl;
// cout.precision(12) postavlja preciznost na 12  znamenaka
cout.precision(12);
cout << "Preciznost: "   << cout.precision()
     << ", Vrijednost: " << sqrt(2.0) << endl;
// možemo upotrijebiti i setprecision manipulator
cout << setprecision(4);
cout << "Preciznost:  "  << cout.precision()
     << ", Vrijednost: " << sqrt(2.0) << endl;

Ispis:

Preciznost:  6, Vrijednost: 1.41421
Preciznost: 12, Vrijednost: 1.41421356237
Preciznost:  4, Vrijednost: 1.414

setprecision manipulatori i drugi manipulatori koji uzimaju argumente definirani su u iomanip headeru.

Eksponencijalni ili pozicijski ispis

Ovdje su nam na raspolaganju manipulatori scientific i fixed. Oni trajno mijenjaju formatiranje fp-brojeva, ali ne postoje negativni manipulatori koji bi vratili prethodno stanje. Zato smo dužni manipulirati sa zastavicama, što opisujemo niže, odnosno, u ovom slučaju je dovoljno zvati unsetf metodu kojoj dajemo vrijednost iz biblioteke nazvanu ios::floatfield:

// postavi defaulne vrijednosti - poništi eventualne prethodne promjene
cout.unsetf(ios::floatfield);
cout << "\nDefaultni prikaz: " << 100*sqrt(2.0) << endl;
cout << "Eksponencijalni prikaz: " << scientific << 100*sqrt(2.0) << "\n"
     << "Pozicijski prikaz: " << fixed << 100*sqrt(2.0) << "\n";
// vrati defaultne vrijednosti
cout.unsetf(ios::floatfield);
cout << "Nakon brisanja zastavica: " << 100*sqrt(2.0) << endl;

Ispis:

Defaultni prikaz: 141.421
Eksponencijalni prikaz: 1.414214e+02
Pozicijski prikaz: 141.421356
Nakon brisanja zastavica: 141.421

Ispis decimalne točke

Manipulatori showpoint i noshowpoint kontroliraju ispis decimalne točke kod cjelobrojnih floating point brojeva. Po defaultu točka se ne ispisuje-

     cout << 10.0 << endl;        // ispisuje 10
     cout << showpoint << 10.0    // ispisuje 10.0000
          << noshowpoint << endl; // povrat na defaultnu vrijednost

Kontrola dopunjavanja bjelinama (padding)

Imamo ove manipulatore:

  • setw postavlja minimalnu širinu za sljedeći numerički ili znakovni podatak;

  • left lijevo pozicionira izlaz;

  • right desno pozicionira izlaz (default);

  • internal kontrolira predznak kod negativnog broja. Znak stavi lijevo, a vrijednost desno;

  • setfill specificira alternativan znak za popunjavanje (umjesto bjeline).

setw, kao endl, ne mijenja stanje streama, i vrijedi samo za sljedeći ispis. setw predstavlja samo minimalni broj znakova, što znači da će podatak biti ispisan s većim brojem znakova ako je to potrebno.

Primjer

// minimum 12 znakova za izlaz
cout << "1 i: " << setw(12) << i << "::" << '\n'
     << "1 d: " << setw(12) << d << "::" << '\n';
// pozicioniraj lijevo
cout << left
     << "2 i: " << setw(12) << i << "::" << '\n'
     << "2 d: " << setw(12) << d << "::" << '\n';
// vrati desno pozicioniranje
cout << right
     << "3 i: " << setw(12) << i << "::" << '\n'
     << "3 d: " << setw(12) << d << "::" << '\n';
// unutrašnje pozicioniranje; prikaži pozitivan broj sa znakom +
cout << internal << showpos
     << "4 i: " << setw(12) << i << "::" << '\n'
     << "4 d: " << setw(12) << d << "::" << '\n';
// promijeni znak za popunjavanje u _
cout << setfill('_')
     << "5 i: " << setw(12) << i << "::" << '\n'
     << "5 d: " << setw(12) << d << "::" << '\n'
     << setfill(' ') // vrati bjelinu kao znak za popunjavanje
     << right       // vrati defaultno pozicioniranje
     << noshowpos;  // ne prikazuj + kod nenegativnog broja (default))

Ispis:

1 i:        -1024::
1 d:      3.14159::
2 i: -1024       ::
2 d: 3.14159     ::
3 i:        -1024::
3 d:      3.14159::
4 i: -       1024::
4 d: +    3.14159::
5 i: -_______1024::
5 d: +____3.14159::

Kontrola ulaznog formatiranja

Standardno ulazna operacija ignorira bjeline (blank, tab, newline, formfeed, carriage return). Na primjer, petlja:

char ch;
while (cin >> ch && ch != 'x')
     cout << ch;
cout << endl;

na podacima

w e rx

izvrši se 3 puta čitajući svaki znak posebno, preskakanjem bjelina (bjelina je delimiter). Izlaz programa je:

wer

Manipulator noskipws čini da ulazni operator čita i bjeline, umjesto da ih preskače. Pomoću skipws manipulatora vraćamo se na normalno učitavanje s preskakanjem bjelina.

cin >> noskipws;   // neka cin ne preskače bjeline
while (cin >> ch && ch != 'x')
     cout << ch;
cout << endl;
cin >> skipws;     // vrati staro stanje

Zadamo li ulaz:

e r t zx
dobivamo izlaz
e r t z

Mijenjanje stanja streama pomoću zastavica

Formatiranje ulaza i izlaza pomoću manipulatora je vrlo praktično, no postoji i drugi način da se postignu isti efekti. Način formatiranja ulaza/izlaza određen je varijablama u klasi ios, tipa ios::fmtflags, koje nazivamo zastavicama (flags) i koje određuju način ispisa cijelih brojeva, bool vrijednosti, floating-point brojeva itd.

Postavljanje pojedine zastavice odgovara primjeni ogovarajućeg manipulatora, dok brisanje zastavice odgovara primjeni suprotnog (poništavajućeg) manipulatora.

Neke od zastavica tvore grupe i posebne maske su definirane da olakšaju rad s njima. Funkcije koje manipuliraju sa zastavicama dane su u sljedećoj tabeli.

Funkcija Značenje

setf(flags)

Postavi flags kao dodatnu zastavicu i vrati prethodno stanje svih zastavica

setf(flags, mask)

Postavi flags kao novu zastavicu grupe identificirane pomoću mask i vrati prethodno stanje svih zastavica

unsetf(flags)

Obriši sve zastavice formatiranja

flags()

Vrati skup zastavica formatiranja

flags(flags)

Postavi zatavice formatiranja na vrijednost flags i vrati prethodno stanje zastavica formatiranja

copyfmt(stream)

Kopiraj sve zastavice formatiranja od stream-a

Vraćanje početnog stanja pomoću zastavica

Metoda flags() omogućava da uzmemo stanje streama prije primjene manipulatora i da ga vrtimo kad smo gotovi. Imamo dvije preopterećene verzije:

  • flags() bez argumenata vraća tekuće stanje formatiranja streama. Vrijednost je tipa fmtflags.

  • flags(arg) uzima fmtflags argument i postavlja stanje stream na ono indicirano argumentom.

Na primjer,

void display(ostream & os)
{
     // Zapamti trenutno stanje streama
     ostream::fmtflags curr_fmt = os.flags();
     // Rad sa streamom ... manipulacije
     os.flags(curr_fmt);      // vrati originalno stanje
}

Funkcije setf() i unsetf() postavljaju ili uklanjaju jednu ili više zastavica. Pri tome je moguće manipulirati s više zastavica kombiniranjem njihovih simboličkih imena pomoću operatora binarno ili (|). Funkcija setf() može uzeti drugi argument kako bi uklonila sve zastavice iz grupe određene drugim argumentom prije no što postavi zastavice određene prvim argumentom, koje moraju pripadati istoj grupi. Na primjer,

// postavi zastavice showpos, showbase i uppercase što je ekvivalentno upotrebi istih manipulatora
cout.setf(ios::showpos | ios::showbase | ios::uppercase);
cout << hex << 11 << endl;
// Postavi samo zastavicu hex u grupi basefield (koja sadrži hex, oct, dec)
cout.setf(ios::hex, ios::basefield);
cout << 12 << endl;
// očisti zastavicu uppercase
cout.unsetf(ios::uppercase);
cout << 12 << endl;

Ispis:

0XB
0XC
0xc

Funkcijom copyfmt() možemo kopirati informacije o formatu iz jednog streama u drugi.

IO klase

Sljedeća slika pokazuje lanac nasljeđivanja za klase streamova.

io_stream_class_diagram.png

Prikazane klase su ustvari aliasi za parametrizirane klase koje u svom imenu imaju "basic_", a parametrizirane su tipom znaka na kojem je string sagrađen. Na primjer,

typedef basic_ios<char>                ios;
typedef basic_istream<char>         istream;
typedef basic_ostream<char>         ostream;
typedef basic_iostream<char>       iostream;
typedef basic_ifstream<char>       ifstream;
typedef basic_ofstream<char>       ofstream;
typedef basic_fstream<char>         fstream;

Kada se za template parametar uzme široki znak (tip wchar_t) dobiva se posve isti sustav klasa čijim imenima prethodi znak "w". Na primjer

typedef basic_ios<wchar_t>            wios;
typedef basic_istream<wchar_t>     wistream;
typedef basic_ostream<wchar_t>     wostream;
typedef basic_iostream<wchar_t>   wiostream;
typedef basic_ifstream<wchar_t>   wifstream;
typedef basic_ofstream<wchar_t>   wofstream;
typedef basic_fstream<wchar_t>     wfstream;

Uočimo da klasa ios_base nije parametrizirana jer sadrži samo dio sustava vezan uz formatiranje i stanje izlazno ulaznih tokova. Pored ovih klasa sustav čini još i parametrizirana klasa basic_streambuf<> koja vrši stvarni transfer podataka između programa i ulaza/izlaza. Sve klase koju proširuju ios_base vrše samo formatiranje podataka. Kao i kod ostalih klasa imamo dva simbolička imena:

typedef basic_streambuf<char>         streambuf;
typedef basic_streambuf<wchar_t>     wstreambuf;