2.3.3 Formatoitu tulostus printf-funktiolla

Formatoidulla tulostuksella tarkoitetaan mm. muuttujien ulkoasun määräämistä tulostusvaiheessa. Esimerkiksi liukuluvusta voidaan määrätä tulostumaan ainoastaan kaksi ensimmäistä desimaalia. Tämä voidaan kyllä tehdä myös cout:lla, mutta ehkä helpommin se onnistuu paljon käytetyllä (C-kieleen kuuluvalla) printf-funktiolla, joka on määritelty stdio-kirjastossa.

Formatoidussa tulostuksessa kääntäjälle annetaan malli, jonka mukaan tulostuksen ulkoasu tulisi muokata. Annettava malli, formaatti, on merkkijono, joka kertoo miten tulostetaan. Yleiseen merkkijonojen tapaan se esitetään "-merkkien välissä. Formaatti voi sisältää vakiotekstejä, eli tekstinpätkiä, jotka kerrasta toiseen tulostuvat samanlaisina, sekä muuntuvia osia, joiden sisältö määräytyy tulostettavan aineiston, eli muuttujien sisällön mukaan.

Tulostusformaatissa voitaisiin määritellä vaikkapa seuraavia asioita (suluissa vastaavan tulostuksen määräävä C++-kielinen formaatti):

Formaatit voidaan kirjoittaa yhtenä kokonaisuutena, jolloin niistä saadaan yhtenäisen lauseen tulostusmuoto (formaatti):

"Appelsiinin hinta on %.2lf mk.\n %d appelsiinia maksaa %.2lf mk.\n"

Formatoitu tulostus näytölle tapahtuu funktiolla printf, jonka yleinen muoto on

printf(formaatti, lausekkeet);

Komennon sisältämään formaattiin voidaan sijoittaa ns. konversiomäärittimiä, jotka kertovat, millä tavoin formaatin perässä esiintyvien lausekkeiden arvot tulee tulkita.

Konversiomäärittimen pakolliset osat ovat konversiomerkki % ja tyypin määritin. Näiden väliin voidaan lisätä ns. lippuja: kentän leveyden määrittely (merkkijonoille, kokonaisluvuille, liukuluvuille) tai tulostettavan arvon tarkkuuden määrittely (liukuluvuille).

Tyypin määritin voi saada mm. seuraavat arvot:

Kentän leveydeksi kirjoitetaan kokonaisluku, joka määrää kuinka monta merkkiä tulostettavalle tiedolle varataan. Kyseessä on minimileveys; jos tulostus ei mahdu annettuun kenttään, leveyttä kasvatetaan tarvittava määrä (siis tulostuksesta ei häviä mitään).

Tarkkuuden määrittely aloitetaan pisteellä, jonka jälkeen kirjoitetaan kokonaisluku, joka osoittaa halutun tarkkuuden. Tarkkuuden määrittelyä voi käyttää joko yhdessä kentän leveyden kanssa tai yksinään.

Parhaiten asia selvinnee esimerkin avulla:

/* *********************************************************
FORM1.CPP
  Tulostetaan näytölle eri tyyppisten muuttujien arvoja.
  Huomaa formaatin vaihtuminen muuttujan tyypin mukaan.
********************************************************* */

#include <stdio.h> // kirjasto, jossa määritelty mm. printf

int main(void)
{
  int luku = 20 ;               // kokonaisluku
  long int isoluku = 23000 ;    // pitkä kokonaisluku
  float numero1 = 7.548;        // liukuluku
  double numero2 = 5.6741;      // kaksoistarkkuuden liukuluku
  char merkki = 'A';            // merkki
  char mjono[] = "ohjelmointi"; // merkkijonot opitaan myöhemmin

  printf("luku    on %5d \n", luku);      // kentän leveys 5
  printf("isoluku on %ld \n", isoluku);   // leveyttä ei annettu
  printf("numero1 on %4.2f \n", numero1); // leveys 4, tarkkuus 2
  printf("numero2 on %lf \n", numero2);   // huomaa oletustarkkuus
  printf("merkki  on %c \n", merkki);
  printf("mjono   on %7s \n", mjono);     // liian lyhyt leveys!

  return 0;
}

Ohjelma tulostus näyttää seuraavalta:

luku    on    20
isoluku on 23000
numero1 on 7.55
numero2 on 5.674100
merkki  on A
mjono   on ohjelmointi

Mikä on esimerkin printf -lauseiden formaateissa esiintyvän \n -osan merkitys?

Jos esimerkin merkkimuuttuja merkki tulostettaisiin kokonaislukuformaatilla,
printf("merkki on %d \n", merkki);
niin mitä lause tulostaisi?

Koska merkki % (prosentti) on varattu konversiomäärittimen aloitusmerkiksi, ei sitä voida tulostaa suoraan, vaan prosenttimerkki saadaan tulostumaan merkkiparilla %%.

Funktion printf kutsussa kunkin formaatin jälkeen mainitun lausekkeen arvo liitetään vuorollaan formaatin sisältämään merkkijonoon konversiomäärittimen määräämässä muodossa. Olkoon muuttujassa luku arvo 7 ja muuttujassa tulos arvo 49. Tällöin lause

printf("%d kertaa %d on %d !!\n", luku, luku, tulos);

antaa tulostukseksi

7 kertaa 7 on 49 !!

Jälleen on huomattava, että formaatin jäljessä esiintyy nimenomaan lausekkeita: Lause

printf("Luvun %d neliö on %d.\n", luku, luku*luku);

tulostaa

Luvun 7 neliö on 49.

Oletuksena liukuluvut tulostetaan kuudella desimaalilla (Borland C++-kääntäjää käytettäessä). Jos double-tyyppiä olevan muuttujan hinta arvo on 2.5, saadaan lauseesta

printf("Appelsiinin hinta on %lf markkaa.\n", hinta);

tuloste

Appelsiinin hinta on 2.500000 markkaa.

Saatu tuloste näyttää huonolta, sillä hinnat yleensä ilmoitetaan kahta desimaalia käyttäen. Lisätään tulostusformaattiin halutuksi tarkkuudeksi kaksi desimaalia. Tällöin lauseesta

printf("Appelsiinin hinta on %.2lf markkaa.\n", hinta);

saadaan tuloste

Appelsiinin hinta on 2.50 markkaa.

Esimerkki

/* *********************************************************
APPELS.CPP
  Lasketaan tuotteen hinnasta alennus, ja tulostetaan vanha
  hinta, aleprosentti ja alennettu hinta.
********************************************************* */

#include <stdio.h>

int main(void)
{
  double hinta = 195.90;
  int ale_pros = 25;

  printf("Entinen hinta on %.2lf mk.\n", hinta);
  printf("Alennus on %d %%.\n", ale_pros);
  printf("Uusi hinta on %.2lf mk.\n", hinta-(ale_pros*hinta)/100);

  return 0;
}

Mitä tekevät esimerkin keskimmäisessä printf -lauseessa peräkkäiset prosenttimerkit %% ?

Jos viimeinen printf -lause olisi muotoa
printf("Uusi hinta on %lf mk.\n", hinta-(ale_pros*hinta)/100);
miten ohjelman toiminta muuttuisi?