58127-1 C-ohjelmointi : 3 Syöttö ja tulostus
- 3.1 Tiedostot
- 3.2 Formatoitu tulostus (printf)
- 3.3 Formatoitu lukeminen (scanf)
- 3.4 Merkeittäin lukeminen (getchar)
- 3.5 Riveittäin lukeminen (gets) ja tulostaminen (puts)
3 Syöttö ja tulostus
Syöttö ja tulostus eivät kuulu itse C-kieleen, vaan välineitä siihen on C-standardiin kuuluvassa kirjastossa stdio. Kirjasto määrittelee kolme tyyppiä, useita makroja ja lukuisan joukon funktioita, jotka suorittavat syöttö- ja tulostustoimintoja. Tyypit ovat FILE ja fpos_t.C-kielen yhteydessä syötön ja tulostuksen kuvaamiseen käytetään tietovirran käsitettä. Tietovirta kuvaa laitteita kuten päätettä tai magneettinauhaa tai levytiedostoja. Tietovirta on käsitteellinen yleistys, jota käytettäessä ei viitata erilaisten laitteiden erikoisominaisuuksiin. Tietovirtoja on kahdenlaisia: tekstivirtoja ja binäärivirtoja.
Tekstivirta on jono merkkejä, jotka muodostavat rivejä. Kunkin rivin lopussa on rivinvaihtomerkki. Rivi voi olla tyhjä, mutta silloinkin sen lopussa on rivinvaihtomerkki. Koska eri tietokoneissa on erilaisia tapoja esittää tekstiä, syöttöä tai tulostusta suorittavat funktiot saattavat muuttaa tekstivirtojen merkkejä taikka lisätä tai poistaa niitä. Tyypillinen esimerkki on rivinvaihtomerkki, jota saattaa vastata yksi erikoismerkki mutta myös erikoismerkkipari. Toteutuksesta riippumatta C-kielessä rivinvaihtomerkin tulee olla '\n', t.s. rivinvaihdon kohdalla esim getchar-funktion tulee palauttaa arvonaan kyseinen merkki.
Tekstivirrasta luettavan tekstin ei välttämättä tarvitse täsmälleen vastata sitä, mitä sinne on aiemmin kirjoitettu, paitsi silloin, kun teksti koostuu kokonaisista riveistä, jotka sisältävät vain kirjoittuvia merkkejä sekä seuraavia ohjausmerkkejä: tabulaattoreita, rivinvaihtoja, pystysuoria tabulointeja ja sivunvaihtoja.
Binäärivirta on jono tavuja, jotka sisältävät tietoa tietokoneen sisäisestä esitysasusta. Binäärivirrasta luettu tieto vastaa täsmälleen sinne aiemmin kirjoitettua. Tulostuksen yhteydessä järjestelmä kuitenkin saattaa lisätä binäärivirtaan nollatavuja (NULL-merkkejä).
3.1 Tiedostot
Tietovirta liitetään ulkoiseen tiedostoon avaamalla tiedosto, mikä voi tarkoittaa myös uuden tiedoston perustamista. Vanhan tiedoston uudelleen perustaminen tuhoaa sen vanhan sisällön.Tiedostoon liittyy tiedosto-osoitin (FILE *), joka osoittaa kohtaa, johon seuraava tiedosto-operaatio kohdistuu. Kun tiedosto on liitetty tietovirtaan, tämä osoitin osoittaa tiedoston alkuun, siis ensimmäiseen merkkiin. Luettaessa tiedostosta ja kirjoitettaessa tiedostoon osoittimen arvo päivittyy automaattisesti.
Kun tietovirta on puskuroimaton, ohjelma lukee tai kirjoittaa merkit välittömästi. Kun tietovirta on puskuroitu, järjestelmä kokoaa merkit ns. puskuriin ja lukee tai kirjoittaa jaksoissa, kun puskuri on täyttynyt.
Tiedoston ja tietovirran välinen yhteys poistetaan sulkemalla tiedosto. Käynnistyessään ohjelma aina avaa automaattisesti kolme ennaltamääriteltyä tekstivirtaa:
- standardisyöttövirta (standard input stream) stdin
- standarditulostusvirta (standard output stream) stdout
- standardivirhevirta (standard error stream) stderr
3.2 Formatoitu tulostus (printf)
Funktiolla printf kirjoitetaan formatoidusti standarditulostusvirtaan stdout. Funktion kutsu on muotoaprintf(formaatti, arg1, ..., argn),missä formaatti on merkkijono, joka määrää tulostuksen ulkoasun, ja arg1, ..., argn ovat lausekkeita, joita voi olla mielivaltainen määrä (myös nolla). Standardin mukaan funktio palauttaa tulostamiensa merkkien määrän tai virheen sattuessa negatiivisen arvon.
Formaatti on merkkijono, joka koostuu sellaisinaan tulostuvista merkeistä ja kentänmäärittelyistä. Argumenttien määrän ja tyyppien tulee vastata kenttämäärittelyitä. Muussa tapauksessa tulos on määrittelemätön (kääntäjä ei välttämättä ilmoita mitään). Kenttämäärittelyt alkavat %-merkillä. Kenttämäärittelyt ja argumentit vastaavat toisiaan annetussa järjestyksessä, so. formaatin k:s kentänmäärittely määrää argumentin argk tulostusmuodon. Kentänmäärittelyt määräävät, mitä muutoksia funktio kohdistaa argumenttiensa arvoihin niitä tulostaessaan.
Koska formaatin muu teksti kuin kentänmäärittelyt tulostuu sellaisenaan, sitä käytetään suorasanaisen tekstin, lukujen välisen tyhjän tilan yms. tulostamiseen. Kentänmäärittely koostuu %-merkistä ja seuraavista osista tässä järjestyksessä:
- Lippuja (flags), jotka modifioivat kentänmäärittelyn merkitystä.
- Minimikentänleveys, joka ilmaistaan kokonaislukuvakiolla. Jos
tulosteessa on kentänleveyttä vähemmän merkkejä, tulee kenttään
täytemerkkejä vasemmalle (tai oikealle, jos formaatissa on
vasemmalletasauslippu). Täytemerkit ovat välilyötejä tai nollia. Jos
tulosteessa on kentänleveyttä enemmän merkkejä, kenttä levenee
tarpeeksi. Jos kentänleveyttä ilmaiseva kokonaislukuvakio alkaa
nollalla, täytemerkkinä on nolla, muuten välilyönti.
- Tarkkuus, joka koostuu pisteestä ja sitä seuraavasta
kokonaisluvusta. Jos pistettä ei seuraa kokonaisluku, rakenne toimii
kuten .0. Tarkkuus ilmoittaa:
- tulostuvien numeroiden vähimmäismäärän kentänmäärittelykirjaimien
d, i ,o, u, x ja X yhteydessä.
- desimaalipisteen jälkeisten numeroiden määrän
kentänmäärittelykirjaimien e, E ja f yhteydessä.
- merkitsevien numeroiden enimmäismäärän kentänmäärittelykirjaimien g
ja G yhteydessä.
- merkkijonosta tulostuvien merkkien enimmäismäärän
kentänmäärittelykirjaimen s yhteydessä.
- tulostuvien numeroiden vähimmäismäärän kentänmäärittelykirjaimien
d, i ,o, u, x ja X yhteydessä.
- Kokomäärite, joka on kirjain h, l tai L. Kokomäärite h ilmoittaa,
että sitä seuraaviin kentänmäärittelykirjaimiin d, i, o, u, x ja X
liittyvät argumentit ovat tyyppiä short tai unsigned short. Kokomäärite
l ilmoittaa, että sitä seuraaviin kentänmäärittelykirjaimiin d, i, o, u,
x ja X liittyvät argumentit ovat tyyppiä long tai unsigned long.
Kokomäärite L ilmoittaa, että sitä seuraaviin kentänmäärittelykirjaimiin
e, E, f, g ja G liittyvät argumentit ovat tyyppiä long double. Jos
kokomäärite esiintyy jonkin muun kentänmäärittelykirjaimen yhteydessä,
se jää vaikutuksetta.
- Kentänmäärittelykirjain, joka on jokin kirjaimista c, d, e, E, f,
g, G, n, o, p, s, u, x X tai %.
Liput, minimikentänleveys, tarkkuus ja kokomäärite ovat valinnaisia. Sen sijaan kentänmäärittelykirjain on pakollinen. Esimerkiksi kentänmäärittelyssä %+7.4lf on lippu +, minimikentänleveys 7, tarkkuus .4, kokomäärite l ja kentänmäärittelykirjain f.
Liput ja niiden merkitys ovat seuraavat:
- -: Vasemmalletasauslippu: tuloste tulee kentän vasempaan reunaan, ja
täytemerkit siis tulevat oikealle kentän loppuun.
- +: Tuloste alkaa aina etumerkillä (+ tai -).
- tyhjä :Tulosteen alkuu tulee välilyönti, jos tulos on
etumerkillinen ja positiivinen. Jos sekä + että tyhjä esiintyvät,
tyhjä-lippu jää huomiotta.
- #: Aiheuttaa vaihtoehtoisen tulostusmuodon, joka riippuu
kentänmäärittelykirjaimesta seuraavasti:
- c, d, i, s, u : ei vaikutusta.
- o, x, X : ellei tuloste ole nolla sen alkuu tulee
vastaavasti 0, ox tai 0X.
- e, E, f, g, G: tulosteeseen tulee aina desimaalipiste,
vaikka sitä ei seuraisi yhtään numeroa; normaalisti desimaalipiste
esiintyy vain, kun sitä seuraa numero.
- g, G: kentän loppuun voi tulla ylimääräisiä nollia.
- c, d, i, s, u : ei vaikutusta.
- 0: Täytemerkkinä käytetään välilyönnin sijasta nollaa. Lippua
voidaan käyttää kentänmäärittelykirjaimien d, i, o, u, x, X, e, E, f, g,
ja G yhteydessä, muille sen toiminta on määrittelemätön. Jos kuitenkin
kentänmäärittelykirjaimien d, i, o, u, x ja X yhteydessä käytetään
tarkkuutta, 0-lippu jätetään huomiotta.
Kentänmäärittelykirjaimet ja niiden merkitys ovat seuraavat:
- d, i : Vastaava kokonaislukuargumentti tulostuu
etumerkillisenä kymmenjärjestelmän kokonaislukuna. Tarkkuusmääritteen
oletusarvon on 1.
- u : Vastaava kokonaislukuargumentti tulostuu
etumerkittömänä kymmenjärjestelmän kokonaislukuna.
- o : Vastaava kokonaislukuargumentti tulostuu
etumerkittömänä oktaalisena kokonaislukuna.
- x, X : Vastaava kokonaislukuargumentti tulostuu
etumerkittömänä heksadesimaalisena kokonaislukuna. Tulosteen mahdolliset
kirjaimet (a-f) ovat pieniä tai isoja sen mukaan, onko
kentänmäärittelykirjaimena x vai X.
- f : Vastaava reaalilukuargumentti tulostuu desimaalimuodossa
[-]ddd.ddd
Desimaalipisteen oikealla puolella olevien numeroiden lukumäärä on se, jonka kenttämäärittelyn sisältämä tarkkuus ilmoittaa, tai sen puuttuessa kuusi (kääntäjäkohtainen). Jos tarkkuus on ilmoitettu nollaksi, desimaalipiste ei tulostu. Jos desimaalipiste esiintyy tulostuksessa, vähintään yksi numero edeltää sitä. Tulostuksessa tapahtuu pyöristys tulostusasun määräämään tarkkuuteen. - e, E: Vastaava realilukuargumentti tulostuu
eksponenttimuodossa
[-]d.ddde±dd
missä on yksi nollasta eroava numero ennen desimaalipistettä ja sen jälkeen tarkkuuden ilmoittama määrä numeroita. Jos tarkkuus on nolla, desimaalipiste ei tulostu. Tulostuksessa tapahtuu pyöristys tulostusasun määräämään tarkkuuteen. Eksponentti sisältää aina vähintään kaksi numeroa. Esityksessä on pieni tai suuri E-kirjain kentänmäärittelykirjaimen mukaan. - g, G: Vastaava reaalilukuargumentti tulostuu f- tai
e-muodossa. Tarkkuus ilmoittaa merkitsevien numeroiden lukumäärän.
Tulostus riippuu argumentin arvosta ja on mahdollisimman tiivis. Tuloste
on eksponenttiesityksen mukainen vain, jos eksponentti on pienempi kuin
-4 tai suurempi tai yhtäsuuri kuin tarkkuus. Ylimääräisiä loppunollia ei
tulostu. Desimaalipiste tulostuu vain, jos sitä seuraa ainakin yksi
numero.
- c: Vastaava argumentti tulostuu yhtenä merkkinä.
- s: Vastaava argumentti tulostuu merkkijonona. Argumentin
tulee olla osoitin merkkijonoon, jonka lopussa on NULL-merkki.
Merkkijonon merkit tulostuvat NULL-merkkiin saakka. Jos tarkkuus
esiintyy, se määrää tulostuvien merkkien enimmäismäärän.
- p: Argumentin tulee olla tyyppiä void *. Sen arvo muuntuu
kirjoittuviksi merkeiksi ao. C-järjestelmässä määritellyllä tavalla.
- n: Vastaava argumentin tulee olla osoitin
kokonaislukumuuttujaan, joka saa arvokseen niiden merkkien lukumäärän,
jotka printf-funktio siihen mennessä tämän kutsun aikana on kirjoittanut
tulostusvirtaan. Mitään tulostusta ei siis tapahdu.
- %: Tulostuu %-merkki.
Tulostetaan kokonaisluku 123 eri formaateilla:
%d :123: %2d :123: %3d :123: %6d : 123: % 6d : 123: %06d :000123: %-6d :123 : %o :173: %x :7b: %X :7B: %#o :0173: %#X :0X7B:Tulostetaan reaaliluku 123.456 eri formaateilla:
%f :123.456001: %.2f :123.46: %2.f :123: %10.4f : 123.4560: %10.5f : 123.45600: %-10.5f :123.45600 : %010.5f :0123.45600: %-+10.5f :+123.45600: %+-10.4f :+123.4560 : %10.0f : 123:
3.3 Formatoitu lukeminen (scanf)
Funktiolla scanf luetaan formatoidusti standardisyöttövirrasta stdin. Funktion kutsu on muotoa:scanf(formaatti,arg1,...,argn);missä formaatti on merkkijono, joka määrää konversion, jonka funktio suorittaa argumentteja arg1,...,argn vastaavalle syötetiedolle. Argumentteja voi olla mielivaltainen määrä. Näiden argumenttien tulee olla osoittimia, sillä ne määräävät, mihin muistipaikkoihin scanf tallentaa lukemansa arvot.
Jos scanf-kutsussa on vähemmän argumentteja, kuin formaatti edellyttää, funktion toiminta on määrittelemätön. Jos taas formaatti loppuu kesken, ylimääräiset argumentit jäävät huomiotta.
Funktio scanf palauttaa arvon EOF, jos on sattunut virhe ennen yhtäkään konversiota. Muuten funktio palauttaa muistiin tallentamiensa tietoalkioiden lukumäärän. Tämä voi olla odotettua pienempi, jopa nolla, jos syötteet eivät vastaa formaattia. Formaatti voi sisältää kolmenlaisia osia:
- Tyhjiä merkkejä (whitespace characters). Tyhjä merkki formaatissa
aiheuttaa sen, että scanf lukee syöteissä mahdollisesti
seuraavina olevia tyhjiä merkkejä, kunnes löytyy ei-tyhjä merkki. Jos
formaatissa on peräkkäin useita tyhjiä merkkejä, niin vaikutus on sama
kuin yhdellä.
- Kentänmäärittelyjä, jotka alkavat %-merkillä ja aiheuttavat
syöttötietojen lukemisen ja muuntamisen. Periaatteessa scanf
lukee merkkejä kunnes a) tiedosto loppuu, b) löytyy tyhjä merkki tai muu
kyseiseen kenttään sopimaton merkki tai c) luettujen merkkien määrä
saavuttaa määritellyn maksimikentänleveyden.
- Muita merkkejä. Kukin merkki aiheuttaa yhden merkin lukemisen
syöttövirrasta. Jos se ei ole sama kuin formaatin sisältämä merkki,
funktion toiminta loppuu ja tämä merkki palaa syöttövirtaan luettavaksi
seuraavalla lukuoperaatiolla.
- Sijoituksenestomerkki *: scanf lukee syöttövirrrasta
normaalisti kentänmäärittelyn mukaisen kentän mutta ei sijoita
konversion tulosta muistiin ao. argumentin osoittamaan paikkaan, siis
tämä kenttä ohittuu.
- Maksimikentänleveys, joka on nollasta eroava etumerkitön
kokonaisluku.
- Kokomäärite, joka on jokin merkeistä h, l tai L.
Kentänmäärittelykirjaimia d, i, n, u, o tai x voi edeltää kirjain h
tarkoittaen sitä, että vastaava argumentti on osoitin muistipaikkaan,
joka on tyyppiä short int tai unsigned short int, taikka kirjain l
tarkoittaen tyyppiä long int tai unsigned long int.
Kentänmäärittelykirjaimia e, f tai g voi edeltää kirjain l tarkoittaen
sitä, että vastaava argumentti on osoitin muistipaikkaan, joka on
tyyppiä double eikä float, tai kirjain L tarkoittaen tyyppiä long
double.
- Kentänmäärittelykirjain, joka on c, d, e, E, f, g, F, i, n, o, p,
s, u, x, X, % tai [. Kentänmäärittelykirjain [ ei esiinny yksin, vaan
sitä seuraa merkkejä, joista viimeinen on ].
- scanf ei tunne kentänmäärittelyissä esiintyvää tarkkuutta
eikä lippumerkkejä.
- printf ei tunne vasenta hakasulkua [.
- printf-funktiossa kentän leveys on minimi,
scanf-funktiossa maksimi
- tähdellä * on eri merkitykset.
- d: Vastaavan argumentin tulee olla osoitin kokonaislukuun.
Syötteen tulee olla kymmenjärjestelmän kokonaisluku. Lukiessaan syötettä
scanf toimii seuraavasti. Ensin ohitetaan alussa olevat tyhjät
merkit laskematta niitä mukaan maksimikentänleveyteen. Luvun edessä voi
olla etumerkki (+ tai -), jota seuraa numero. Sen jälkeen luetaan
merkkejä, kunnes tiedosto loppuu tai vastaan tulee merkki, joka ei ole
numero tai on luettu maksimikentänleveyden verran merkkejä. Jos luettu
merkkijono on tyhjä, seuraa paluu koko funktion kyseisestä kutsusta.
Luetut merkit tulkitaan kymmenjärjestelmän kokonaisluvuiksi. Merkkijono
muunnetaan etumerkilliseksi kokonaisluvuksi ja sijoitetaan argumentin
osoittamaan paikkaan (ei tarkisteta onko tilaa varattu). scanf
toimii analogisella tavalla lukiessaan syötteitä, jotka vastaavat
kentänmäärittelykirjaimia i, u, o, x ja X.
- i: Vastaavan argumentin tulee olla osoitin kokonaislukuun.
Syötteen tulee olla kymmenjärjestelmän kokonaisluku, oktaalinen
kokonaisluku tai heksadesimaalinen kokonaisluku.
- u: Vastaavan argumentin tulee olla osoitin etumerkittömään
kokonaislukuun. Syötteen tulee olla etumerkitön kymmenjärjestelmän
kokonaisluku.
- o: Vastaavan argumentin tulee olla osoitin kokonaislukuun.
Syötteen tulee olla oktaalinen kokonaisluku.
- x, X: Vastaavan argumentin tulee olla osoitin
kokonaislukuun. Syötteen tulee olla heksadesimaalinen kokonaisluku.
kentänmäärittelykirjaimet x ja X ovat täysin identtisiä ja kummatkin
hyväksyvät numeroiden lisäksi kirjaimet abcdefABCDEF.
- e, f, g: Vastaavan argumentin tulee olla osoitin
realilukuun. Syötteen tulee olla desimaalimuodossa tai
eksponenttimuodossa oleva realiluku.
- s: Vastaavan argumentin tulee olla merkkijono-osoitin (char
*). Ensin scanf ohittaa alussa olevat tyhjä merkit laskematta
niitä kentänleveyteen. Sitten luetaan merkkejä, kunnes
- tiedosto loppuu tai
- vastaan tulee tyhjä merkki tai
- formaatin mukainen maksimikentänleveys tulee täyteen
- c: Vastaavan argumentin tulee olla osoitin merkkiin (char
*). Syöte on yksi tai useampi merkki. Huomaa, että nyt ei ohiteta tyhjiä
merkkejä. Jos kentän leveyttä ei ole määritelty, luetaan täsmälleen yksi
merkki.
- n: Vastaavan argumentin tulee olla osoitin kokonaislukuun ja
arvoksi tulee virrasta luettujen merkkien lukumäärä.
- %: Syöttövirrasta odotetaan löytyvän %-merkin. Mitään
sijoitusta ei tapahdu.
#include<stdio.h> int main(void) { int n,i; float x; char nimi[10]; n = scanf("%d%f%s",&i,&x,nimi); }Jos annemme syötteenä rivin
78 15.23e-1 Terotällöin funktio palauttaa arvon 3, muuttuja i saa arvon 78, muuttuja x saa arvon 1.523 ja nimi arvon "Tero".
#include<stdio.h> int main(void) { int n,i; float x; char sana[20]; n = scanf("%2d%f%*d %[akKu]",&i,&x,sana); }
Jos annamme syötteenä rivin
123456 0987 Marjaniin funktio palauttaa arvon 3, muuttuja i saa arvon 12, muuttuja x saa arvon 3456.0 ja sana saa arvon "Marja".
3.4 Merkeittäin lukeminen (getchar)
Funktion getchar määrittely on muotoaint getchar(void);Funktion getchar kutsu vastaa sellaista funktion getc (Muotoa int getc(FILE *tietovirta) ) kutsua, jossa argumentin tietovirta arvo on stdin. Funktio lukee tietovirrasta seuraavan merkin etumerkittömänä merkkinä, muuntaa sen kokonaisluvuksi (int) ja kasvattaa asianmukaisesti tiedosto-osoitinta. Funktio palauttaa tämän merkin. Jos tietovirta on lopussa, tiedostonloppuindikaattori asettuu ja palautetaan arvo EOF. Samoin käy, jos lukemisessa tapahtuu virhe. Esimerkiksi:
#include<stdio.h> int main(void) { int c; while(( c = getchar() ) != EOF ) putchar(c); return 0; }
3.5 Riveittäin lukeminen (gets) ja tulostaminen (puts)
Funktion gets määrittely on muotoachar *gets(char *s);Funktio lukee merkkejä standardisyöttövirrasta (stdin) taulukkoon, jota osoittaa s, kunnes tulee vastaan tiedoston loppu tai rivinvaihto. Funktio lisää tallentamansa merkkijonon perään NULL-merkin mutta ei rivinvaihtomerkkiä. Funktio palauttaa osoittimen s, jos kaikki menee hyvin. Jos heti tulee vastaan tiedoston loppu, taulukon sisältö pysyy muuttumattomana ja funktio palauttaa nollaosoitteen.
Funktion puts määrittely on muotoa
int puts(char *s);Funktio kirjoittaa s:n osoittaman merkkijonon standariditulostusvirtaan (stdout) ja lisää perään rivinvaihdon. Jos sattuu kirjoitusvirhe, funktio palauttaa arvon EOF, muuten funktio palauttaa positiivisen arvon. Esimerkki:
#include<stdio.h> #define LINESIZE 80 int main(void) { char line[LINESIZE + 1]; int linenumber = 1; while( gets(line) != NULL ) { printf("%6d\t",linenumber++); puts(line); } return 0; }
Jan Lindström (Jan.Lindstrom@cs.Helsinki.FI)