Syöte- ja tulostus C-kielessä¶
Osaamistavoitteet: Tämän materiaalin luettuasi osaat tuottaa muotoiltua tulostusta sekä kysyä käyttäjältä syötettä C-kielellä.
Aiemmassa materiaalissa olemme jo onnistuneet tulostamaan tekstiä tietokoneen ruudulle C-kielellä printf-lauseella. Seuraavaksi paneudutaan asiaan hieman syvemmälti ja katsotaan miten tehdään muotoiltua tulostusta. Tässä yhteydessä esitellään C:n standardikirjaston
stdio
muotoiltuun tulostukseen ja syötteen lukemiseen (näppäimistöltä) tehdyt valmiit funktiot. Koska funktiot ovat osa standardia, joten ne toimivat kaikissa järjestelmissä samalla tavoin. Noo.. siltikään ne eivät toimi aivan samoin kaikissa järjestelmissä. Sulautetuissa järjestelmissä esiintyy standardikirjastojen toteutuksia, jossa kaikentyyppisen datan käsittely ei onnistu. Laitteiden resurssirajoitusten takia esimerkiksi liukulukujen käsittely ja tulostus voi jäädä pois. Noh, tätä asiaa murehdimme myöhemmin.
Emme vielä tässä vaiheessa tiedä kaikkea C-kielen detaljeista, mutta pärjäämme näiden funktioiden kanssa aivan mainiosti.
Muotoiltu tulostus¶
C-kielessä on useita funktioita, joilla voidaan tuottaa muotoiltua tulostusta eri tilanteisiin. Käsittelemme tässä kaksi tapausta. Myöhemmin käy ilmi, että sulatetuissa järjestelmissä voi olla omia toteutuksia näille funktioille, jotka tulostavat tavaraa milloin millekin oheislaitteelle.
Funktio printf¶
printf:n syntaksi on seuraava:
printf(muotoilumerkkijono, argumentti1, argumentti2, ...);
Muotoilumerkkijono kertoo meille, mitä tulostetaan ja miten argumentit muokataan tulostukseen. Niiden perässä on epälukuinen joukko argumenttejä, jotka voivat olla esimerkiksi muuttujien arvoja jotka haluamme tulostaa. Argumenttejä voi siis olla kymmeniä yhdessä printf-funktiokutsussa, mutta emme kurssilla lähde testaamaan C-kääntäjää..
Katsotaanpa esimerkkiä printf:n käytössä..
uint8_t a=97;
printf("Luku on %d\n", a);
// Tulostaa
Luku on 97 (+rivinvaihto)
printf:n ensimmäinen argumentti on siis muotoilu
"Luku on %d\n"
. Tämä tulostaa annetut merkit kuten ne on merkkijonon laitettu, paitsi kun %
-merkillä kerromme argumentin paikan ja muotoilumääreen. Nyt meillä funktiokutsussa on yksi argumentti a
, jonka paikan tulostuksessa ja muotoilun kerromme määreellä %d
. Kaksi muuttujaa voitaisiin tulostaa seuraavasti printf("Luvut ovat %d ja %d\n", a, b);
Näitä muotoilumääreitä on C-kielessä koko joukko, tässä alla yleisimmät.
Tässä tulee nyt huomata, että printf itseasiassa tekee tarvittaessa muunnoksen argumentin muuttujatyypistä muotoilun muuttujatyyppiin! Tällöin esimerkiksi yllä esitelty muuttuja
int a=97
voidaan tulostaa merkkinä määreellä %c
. (Muistamme tässäkohdin ASCII-taulukon.) Jotta asiaan saadaan mutkia matkaan, niin printf-muunnos tekee pyöristyksen muuttujatyypin ja muotoilun välillä, eikä käytä sitä kuuluisaa giljotiinia. Muotoilua voidaan myös tarkentaa..
Esimerkiksi.
float pii = 3.14159;
printf("Pii on kahdella desimaalilla %.2f\n", pii);
// Tulostaa
Pii on kahdella desimaalilla 3.14 ( + rivinvaihto)
Muotoilumääreessä on vielä yksi asia, nimittäin lopussa oleva merkkivakio
\n
, joka tarkoittaa rivinvaihtoa. Eli kääntäjä asettaa tulotukseen tälle kohdin rivinvaihdon. Merkkivakiot voivat toki olla missä kohti tulostusta hyvänsä. Alla lisää merkkivakioita (näistäkään emme nyt tarjoa täydellistä esitystä).Huomataan!, että kääntäjä joskus valittaa funktioiden argumenttien vääristä tyypeistä kun käytämme johdettuja muuttujatyyppejä. Tämän korostuu printf:n ja scanf:n kanssa koska ne ovat pääosin työasemaohjelmointiin kehitettyjä funktiota. Voimme poistaa näitä varoituksia esim. kappaleen 4. materiaalissa esitetyllä tyyppimuunnoksella.
Sulautetulla laitteella ohjelmoidessa joudumme vain elämään näiden varoitusten kanssa, mutta kuten sanottu, niin siellä ei esim. standardikirjaston printf-funktiolle oli juuri käyttöä vaan laitteissa on omat toteutukset tulostusfunktioista.
Funktio sprintf¶
Alussa lupasimme käsitellä kaksi eri printf-funktiota, joten esittelemme niistä nyt toisen, eli
sprintf
-funktion. Se toimii muuten samoin kuin printf, mutta siinä on yksi lisäargumentti, eli merkkijono johon tulostus menee. Ideana on siis muotoilla tekstiä valmiiksi sovituissa rajoissa. Tälle funktiolle on oma tärkeä käyttönsä sulautetuissa järjestelmissä, esimerkiksi kun tulostetaan LCD-näytölle tekstiä tai lähetetään langattomalla radiolla viestiä toiselle koneelle. Usein valmiiden ohjelmakirjastojen funktiot, esimerkiksi viestin lähetykseen, ottavat argumentiksi valmiin käsitellyn merkkijonon, eikä laitteelle voi tulostaa suoraan printf:llä tai jollain muulla tulostusfunktiolla. Tällöin sprintf:llä ja muotoilumääreillä on helppo tehdä juuri speksien mukainen viesti merkkijonoon. Kurssin harjoitustyössä tullaan tarvitsemaan sprintf-funktiota ihan varmasti.
sprintf toimii seuraavasti:
uint8_t a=97;
char str[80]; // Tässä täytyy merkkijonolla olla ehdottomasti määritelty pituus
sprintf(str,"Muuttuja a on samaan aikaan %04d ja '%c', molemmat ovat %d%% oikein\n",a,a,100);
Nyt merkkijono-taulukko
str
:n tallentui muotoilumääreen mukainen tulostus! Nyt str voidaan antaa argumentiksi toiselle funktiolle. Muotoilumääreiden kanssa täytyy ohjelmoijan huomioida merkkijono-taulukon pituus, jottä esimerkiksi erikokoiset muuttujien numeeriset arvot mahtuvat siihen. Muuten seurauksena on taas muistin ylivuoto tai tapahtuu muuta outoa. Esimerkiksi sulautetun laitteen LCD-näytön leveys voi olla 16 merkkiä ja tulostus pitää sovittaa siihen.
Vakiosyöte¶
Funktio
getchar
tarjoaa keinon kysyä käyttäjältä merkki kerrallaan. Funktio esitellään myös stdio.h
-kirjastossa. Seuraavalla do-while-rakenteella voimme kysyä käyttäjältä merkkejä, kunnes käyttäjä painaa Enter-näppäintä (suom. palautusnäppäin..).
uint8_t c = 0;
do {
c = getchar();
} while (c != '\n');
..eli on
do-while
-rakenteella puolensakin. Huomataan, että kun työasemalla testaamme ohjelmaa, näppäimistö kaiuttaa näppäinpainallukset ruudulle näkyviin. Noh, tämä on käyttöjärjestelmän ominaisuus ja sen kanssa joudumme elämään.
Muotoiltu syöte¶
Funktio
scanf
on standardikirjaston funktio jolla voidaan kysyä käyttäjältä muotoiltua syötettä (ja lennosta muuntaa) ja tallentaa syöte muuttujiin. Sen esittely on samankaltainen kuin printf:n, pienillä, mutta oleellisilla eroilla.scanf(muotoilumerkkijono, &muuttuja1, &muuttuja2, ...);
Muotoilumerkkijonon syntaksi on ihan sama kuin printf:n, eli käytössä on samat muotoilumääreet. Argumentissa annettavien muuttujien nimen eteen tarvitaan
&
-operaattori. Tästä seuraa, että funktion argumentit eivät olekaan muuttujia, vaan niiden osoitteita! Ok, tästä lisää osoittimien yhteydessä, nyt riittää että tiedostamme tämän seikan. Esimerkiksi, tässä kysytään uint32_t-tyyppinen arvo käyttäjältä ja tulostetaan se takaisin ruudulle. Ja joo, scanf edellyttää että syöte lopetetaan Enter:n painalluksella.
uint32_t iso=0;
scanf("%ld",&iso); // Huomaa %ld-määre ja &-operaattori muuttujanimen edessä.
printf("%ld\n",iso);
scanf on paljon monikäyttöisempi kuin pelkän yhden numeron kysely, sillä voidaan kysyä pitempiä muotoiltuja syötteitäkin!
uint8_t paiva=0;
char kuukausi[12];
uint16_t vuosi=0;
printf("Anna paivamaara muodossa: 24.Joulukuuta 2017? ");
scanf("%2d.%s %4d", &paiva, kuukausi, &vuosi); // Huomaa &-operaattori, paitsi kuukausi-muuttujassa!
printf("Annoit %d.%s %d\n", paiva, kuukausi, vuosi);
Nyt muotoilumerkkijono
%2d.%s %4d
on huomattavasti tarkempi, se ottaa vastaan max. kaksinumeroisen kokonaisluvun muuttujaan paiva
, sen jälkeeen se odottaa pistettä ja sen jälkeen merkkijonoa muuttujaan kuukausi
. Ja lopuksi scanf odottaa välilyönnin jälkeen max. nelinumeroista lukua muuttujaan vuosi
. Eli muotoilumerkkijono tulkitaan todellakin kirjaimellisesti. Argumenttien tyypeissäkin on nyt eroja, merkkijono-muuttujien kanssa ei tarvitse &-operaattoria. Miksi ei? ..maltetaan vielä hetki niin asia selviää osoitin-muuttujamateriaalissa. scanf:n muotoilu on kuitenkin helppo rikkoa, jos vaikka kirjoitat annat päivämääräksi kolminumeroisen luvun, tallentuu paiva-muuttujaan vain sen kaksi ensimmäistä numeroa, mutta kolmas numero jää syötteeseen ja sotkee loput siitä. Samoiten, jos jätät pisteen pois syötteestä, scanf menee sekaisin. Kokeile!
Vähemmän yllättäen, vastaavasti sprintf-funktiota, syötteen merkkijonoon tallennukseen löytyy funktio
sscanf
. Nyt ohjelmassa käyttäjän syöte kerättäisiin ensin talteen merkkijonoon jolloin sitä voidaan käsitellä merkkijonona myöhemmin. Esimerkiksi etsiä annetusta syötteestä tiettyjä sanoja.Hox! Todella yleinen virhe on unohtaa
&
-operaattori scanf:n argumenttiluettelosta. Tätä ei joskus edes kääntäjä huomaa, mutta ohjelman suorituksessa asia huomataan kyllä. Lopuksi¶
printf:n muotoilun salaisuuksista kiinnostuneille lisätietoa löytyy mm. dokumentista Secrets of printf.
Anna palautetta
Kommentteja materiaalista?