2.3.2 Syöttö cin-oliolla

Muuttujille voidaan lukea arvoja näppäimistöltä seuraavasti: Olkoon luku kokonaislukumuuttuja. Jos ohjelmassa on lause

cin >> luku;

jää ohjelma odottamaan syöttöä näppäimistöltä. Kun käyttäjä antaa jonkin kokonaisluvun ja painaa Return-näppäintä, ohjelman suoritus jälleen jatkuu. Tällöin muuttujassa luku on juuri näppäilty lukuarvo.

Koska pelkkä cin ei tulosta minkäänlaista kehotetta käyttäjälle, kannattaa syötteitä luettaessa käyttää seuraavanlaista tyyliä:

cout << "Anna luku > ";
cin >> luku;

Tällöin näytölle tulostuu ensin teksti "Anna luku > ", jolloin käyttäjä tietää heti, mitä pitäisi tehdä.

Kuten cout, osaa myös cin tunnistaa itse muuttujan tyypin, ts. se tulkitsee (jos vain mahdollista) annetun syötteen sen mukaan, minkä tyyppiseen muuttujaan se on tarkoitus lukea.

Samoin useita syötteitä voidaan lukea samalla kertaa:

cin >> luku1 >> luku2 >> luku3;

Silloin kaikki syötettävät luvut kirjoitetaan samalle riville välilyönneillä eroteltuna ja syöttö lopetetaan, kuten aina, painamalla Return-näppäintä.

2.3.2.1 Kun syötetään väärin

Olkoonkin, että cin:llä lukeminen on suhteellisen vaivatonta ja yksinkertaista, ei siihen pidä luottaa liikaa. Virheitä tapahtuu esimerkiksi silloin, kun käyttäjän näppäilemä syöte ei täsmää muuttujan tyyppiin: Jos kokonaislukumuuttujaan luku luetaan arvo lauseella

cin >> luku;

ja syöttäjä antaa arvoksi

heipä hei

ei ohjelma muuta muuttujalla luku entuudestaan ollutta arvoa miksikään (siis pahimmassa tapauksessa sen arvo voi olla jotain täysin sattumanvaraista), sillä syötettä ei kerta kaikkiaan voi tulkita mitenkään kokonaisluvuksi.

Samoin täytyy olla tarkkana, ettei anna liian "pitkää" syötettä: Tarkastellaan esimerkkinä seuraavaa ohjelmanpätkää (ei vielä toimiva ohjelma; lisää itse tarvittavat osat!):

cout << "Anna kaksi lukua > ";
cin >> luku1 >> luku2;
cout << "Anna vielä yksi luku > ";
cin >> luku3;
cout << "Annoit luvut " << luku1 << ", ";
cout << luku2 << " ja " << luku3 << ".\n";

Kokeillaan syöttää kolme kokonaislukua:

Anna kaksi lukua > 3 5
Anna vielä yksi luku > 7
Annoit luvut 3, 5 ja 7.

Näyttää toimivan mainiosti. Kokeillaanpa vielä:

Anna kaksi lukua > 3 5 7
Anna vielä yksi luku > Annoit luvut 3, 5 ja 7.

Mitä tapahtui? Ensimmäinen cin lukee syötteestä kaksi kokonaislukua, 3 ja 5. Kaikki, mitä käyttäjä on kirjoittanut tämän lisäksi samalle riville, jää edelleen ns. puskuriin. Toinen cin jatkaa lukemista siitä, mihin jäätiin. Koska seuraavana on kokonaisluvuksi kelpaava syöte 7, luetaan se muuttujaan luku3 eikä jälkimmäisen cin:n tarvitse enää pysähtyä odottamaan uutta syöttöä.

Tämä voidaan korjata käyttämällä cin:n ns. jäsenfunktiota ignore, joka ohittaa halutun määrän merkkejä syötteestä. Esimerkiksi funktiokutsulla cin.ignore(80,'\n')ohitetaan korkeintaan 80 merkkiä aina rivinvaihtomerkkiin saakka (joka myös ohitetaan). Muutetaan ohjelmapätkä muotoon

cout << "Anna kaksi lukua > ";
cin >> luku1 >> luku2;
cin.ignore(80, '\n');
cout << "Anna vielä yksi luku > ";
cin >> luku3;
cout << "Annoit luvut " << luku1 << ", ";
cout << luku2 << " ja " << luku3 << ".\n";

ja kokeillaan uudestaan:

Anna kaksi lukua > 3 5 7
Anna vielä yksi luku > 9
Annoit luvut 3, 5 ja 9.

Nyt näyttää toimivan niin kuin oli tarkoituskin: Kun ensimmäinen cin on lukenut syötteestä kaksi kokonaislukua, ohitetaan kaikki sen jälkeen samalla riville kirjoitettu (7 ja rivinvaihto), jolloin myös jälkimmäinen cin pyytää uutta syötettä.

2.3.2.2 Merkkitiedon lukeminen

Myös merkkimuuttujaan voidaan lukea arvoja cin:llä. Mutta koska välilyönti toimii syötössä erottimena, aiheutuu tästä ongelmia. Mitä jos halutaankin lukea välilyöntimerkki? Tällöin voidaan käyttää cin:n jäsenfunktiota get. Jos merkki on char tyyppinen muuttuja, lukee lause

cin.get(merkki);

siihen täsmälleen yhden merkin syötteestä, riippumatta siitä mikä merkki on vuorossa. Huomaa, että tällöin >>-operaattoria ei käytetä.

Myös merkkijonojen lukemisessa on sama ongelma. Määritellään merkkijonomuuttuja nimi seuraavasti:

char nimi[50];

Tarkastellaan ohjelmanpätkää

cout << "Anna nimesi > ";
cin >> nimi;
cout << "Nimesi on " << nimi << "." << endl;

ja kokeillaan sitä:

Anna nimesi > Ossi Opiskelija
Nimesi on Ossi.

Koska välilyönti toimii erottimena, cin lopettaa lukemisen heti etunimen jälkeen. Tämä voidaan korjata käyttämällä cin:n jäsenfunktiota getline, jolla pystyy lukemaan kokonaisen rivin:

cout << "Anna nimesi > ";
cin.getline(nimi, 50);
cout << "Nimesi on " << nimi << "." << endl;

Tässä siis merkkijonoon nimi luetaan merkkejä kunnes on luettu korkeintaan 49 merkkiä (loppumerkille \0 jätetään yksi paikka) tai kunnes vastaan tulee rivinvaihtomerkki (joka ohitetaan).

Anna nimesi > Ossi Opiskelija
Nimesi on Ossi Opiskelija.

Nyt ohjelma näyttää toimivan niin kuin haluttiin. Merkkijonot kannattaa siis lukea riveittäin seuraavan mallin mukaisesti:

/* *********************************************************
MJONO.CPP
  Lukee näppäimistöltä merkkijonon ja tulostaa sen näytölle.
********************************************************* */

#include <iostream.h>

#define MAXPIT 80 // maksimipituus, loppumerkki mukaanlukien

int main(void)
{
  char mjono[MAXPIT];

  cout << "Anna merkkijono    > ";
  cin.getline(mjono, MAXPIT); // lukee merkkijonoon mjono
                              // korkeintaan MAXPIT-1 merkkiä

  cout << "Annoit merkkijonon > ";
  cout << mjono << endl;

  return 0;
}