Tie koodariksi

C++-ohjelmointi

Luku 15: Virrat

Standardivirrat

C++:n standardivirrat ovat cin (lukeminen) ja cout (tulostaminen). Oletuksena lukeminen tarkoittaa, että käyttäjä antaa tietoa, ja tulostaminen tarkoittaa, että tietoa tulostetaan näytölle.

Kuitenkin standardivirtoihin voi ohjata myös tiedostoja. Esimerkiksi kun Linuxissa ohjelman käynnistää näin, tietoa luetaan tiedostosta syote.txt eikä käyttäjältä:

./koodi < syote.txt
Vastaavasti tulostuksen voi ohjata tiedostoon tuloste.txt näin:
./koodi > tuloste.txt
On myös mahdollista yhdistää nämä ohjaukset näin:
./koodi < syote.txt > tuloste.txt

Lukeminen

Kun virrasta luetaan tietoa, jokaisen luettavan alkion välillä tulee olla jotain tyhjää (kuten välilyönti tai rivinvaihto), mutta ei ole väliä, paljonko tyhjää tilaa on. Esimerkiksi koodi
int a, b;
cin >> a >> b;
pystyy lukemaan kaksi kokonaislukua molemmista seuraavista syötteistä:
99 123
    99
  123
Jos luettavien alkioiden määrä ei ole tiedossa etukäteen, seuraavanlainen koodi on kätevä:
int x;
while (cin >> x) {
    cout << "luettiin luku " << x << "\n";
}
Tämä koodi lukee virrasta kokonaislukuja yksi kerrallaan muuttujaan x, kunnes niitä ei enää tule (esimerkiksi jos virtaan on ohjattu tiedosto ja tiedosto päättyy).

Funktio getline lukee virrasta merkkijonoon kokonaisen rivin, mukaan lukien välilyönnit. Esimerkiksi seuraava koodi lukee virrasta rivin muuttujaan s:

string s;
getline(cin, s);

Tulostaminen

#include <iomanip>
Tulostuksen muotoiluun pystyy vaikuttamaan lähettämällä virtaan manipulaattoreita ennen tulostettavaa alkiota. Esimerkiksi manipulaattori setw määrittää seuraavaksi tulostettavan alkion kentän leveyden:
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= 5; j++) {
        cout << setw(4) << i*j;
    }
    cout << "\n";
}
   1   2   3   4   5
   2   4   6   8  10
   3   6   9  12  15
   4   8  12  16  20
   5  10  15  20  25
Manipulaattori setfill määrittää, millä merkillä kentän alkuosa täytetään. Esimerkiksi seuraava koodi tulostaa kellonajan muodossa 08:30:00:
int h = 8, m = 30, s = 0;
cout << setfill('0');
cout << setw(2) << h << ":";
cout << setw(2) << m << ":";
cout << setw(2) << s << "\n";
Liukulukujen kanssa käteviä manipulaattoreita ovat fixed ja setprecision. Kun virtaan lähetetään ensin manipulaattori fixed, tämän jälkeen manipulaattorin setprecision avulla voi määrittää, monenko desimaalin tarkkuudella seuraavat alkiot tulostetaan.
double x = 3.141592653589793;
cout << fixed;
cout << setprecision(0) << x << "\n"; // 3
cout << setprecision(2) << x << "\n"; // 3.14
cout << setprecision(5) << x << "\n"; // 3.14159
Huomaa, että setw vaikuttaa vain seuraavaksi tulostettavaan alkioon, mutta muut yllä esitellyt manipulaattorit vaikuttavat kaikkeen tulostukseen siitä lähtien.

Tiedostojen käsittely

#include <fstream>
Tiedostovirta ifstream lukee tietoa annetusta tiedostosta. Esimerkiksi seuraava koodi lukee kaksi kokonaislukua tiedostosta syote.txt:
ifstream f("syote.txt");
int a, b;
f >> a >> b;
f.close();
Vastaavasti tiedostovirta ofstream tulostaa tietoa annettuun tiedostoon. Esimerkiksi seuraava koodi tulostaa rivin "Moikka!" tiedostoon tuloste.txt.
ofstream f("tuloste.txt");
f << "Moikka!\n";
f.close();
Oletuksena tiedosto tyhjentyy ennen tulostamisen aloittamista. Kuitenkin parametrin ios::app (append) avulla voi määrittää, että uusi tieto tulostetaan tiedoston loppuun:
ofstream f("tuloste.txt", ios::app);

Merkkijonovirta

#include <sstream>
Merkkijonovirran avulla merkkijonossa olevaa tietoa voi lukea muuttujiin. Esimerkiksi seuraava koodi luo merkkijonovirran ja lukee sen avulla kaksi kokonaislukua.
string s = "  99  123 ";
stringstream ss(s);
int a, b;
ss >> a >> b;
cout << a << " " << b << "\n"; // 99 123
Seuraava koodi puolestaan muodostaa merkkijonovirran avulla merkkijonon, jossa on liukuluku esitettynä kahden desimaalin tarkkuudella.
stringstream ss;
double x = 3.141592653589793;
ss << fixed << setprecision(2) << x;
string s = ss.str();
cout << s << "\n"; // 3.14