Tiedosto. Tiedostot ovat joko binääritiedostoja tai tekstitiedostoja. Tekstitiedostot



Samankaltaiset tiedostot
Tiedostot. Tiedostot. Tiedostot. Tiedostot. Tiedostot. Tiedostot

3 Tee ohjelma, joka tulostaa kahden opiskelijan nimet ja osoitteet rinnakkain. 4 Tee ohjelma, joka kysyy käyttäjältä numeron ja tulostaa sen näytölle.

Ohjelmoinnin perusteet Y Python

Merkkijono määritellään kuten muutkin taulukot, mutta tilaa on varattava yksi ylimääräinen paikka lopetusmerkille:

C-kielessä taulukko on joukko peräkkäisiä muistipaikkoja, jotka kaikki pystyvät tallettamaan samaa tyyppiä olevaa tietoa.

Tiedosto on yhteenkuuluvien tietojen joukko, joka tavallisimmin sijaitsee kiintolevyllä, muistitikulla tai jollakin muulla fyysisellä tietovälineellä.

Tietueet. Tietueiden määrittely

Ohjelmoinnin perusteet Y Python

Osoitin ja viittaus C++:ssa

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Pythonin Kertaus. Cse-a1130. Tietotekniikka Sovelluksissa. Versio 0.01b

Ohjelmoinnin perusteet Y Python

Kirjoita oma versio funktioista strcpy ja strcat, jotka saavat parametrinaan kaksi merkkiosoitinta.

Rakenteiset tietotyypit Moniulotteiset taulukot

Ohjelmoinnin perusteet Y Python

C-ohjelma. C-ohjelma. C-ohjelma. C-ohjelma. C-ohjelma. C-ohjelma. Operaatioiden suoritusjärjestys

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

Ohjelmoinnin perusteet Y Python

Zeon PDF Driver Trial

Moduli 5: Kehittyneitä piirteitä

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

Loppukurssin järjestelyt

Loppukurssin järjestelyt C:n edistyneet piirteet

Ohjelmoinnin perusteet Y Python

// // whiledemov1.c // #include <stdio.h> int main(void){ int luku1 = -1; int luku2 = -1;

Ohjelmoinnin peruskurssi Y1

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo

Ohjelmassa muuttujalla on nimi ja arvo. Kääntäjä ja linkkeri varaavat muistilohkon, jonne muuttujan arvo talletetaan.

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Binäärioperaatiot Tiedostot ja I/O

Luento 5. Timo Savola. 28. huhtikuuta 2006

Harjoitustyö: virtuaalikone

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

AS C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin

Ohjelmoinnin perusteet Y Python

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Binäärioperaatiot Tiedostot ja I/O

ATK tähtitieteessä. Osa 4 - IDL input/output. 19. syyskuuta 2014

Osa. Listaus 2.1. HELLO.CPP esittelee C++ -ohjelman osat. 14: #include <iostream.h> 15: 16: int main() 17: {

Maastotietokannan torrent-jakelun shapefile-tiedostojen purkaminen zip-arkistoista Windows-komentojonoilla

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

ITKP102 Ohjelmointi 1 (6 op)

Kirjoita, tallenna, käännä ja suorita alla esitelty ohjelma, joka tervehtii käyttäjäänsä.

Muuttujien roolit Kiintoarvo cin >> r;

Taulukot. Jukka Harju, Jukka Juslin

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssi Y1

Java-kielen perusteet

Metropolia ammattikorkeakoulu TI00AA : Ohjelmointi Kotitehtävät 3

Matriisit ovat matlabin perustietotyyppejä. Yksinkertaisimmillaan voimme esitellä ja tallentaa 1x1 vektorin seuraavasti: >> a = 9.81 a = 9.

5.6. C-kielen perusteet, osa 6/8, Taulukko , pva, kuvat jma

Esimerkkiprojekti. Mallivastauksen löydät Wroxin www-sivuilta. Kenttä Tyyppi Max.pituus Rajoitukset/Kommentit

#include <stdio.h> // io-toiminnot. //#define KM_MAILISSA int main( ){

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä

2 Konekieli, aliohjelmat, keskeytykset

Muistin käyttö. Muistin käyttö. Muistin käyttö. Muistin käyttö. Muistin käyttö. Muistin käyttö. Muistin käyttö C-ohjelmassa

T Sovellusohjelmat Matlab osa 4: Skriptit, funktiot ja kontrollirakenteet

Lyhyt kertaus osoittimista

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Moduli 4: Moniulotteiset taulukot & Bittioperaatiot

12. Näppäimistöltä lukeminen 12.1

Luento 4. Timo Savola. 21. huhtikuuta 2006

Johdatus ohjelmointiin / Lausekielinen ohjelmointi 1 & 2

Hieman linkkejä: lyhyt ohje komentoriviohjelmointiin.

Ohjelmoinnin perusteet Y Python

C-ohjelmointi, syksy 2006

C-ohjelmointi, syksy Yksiulotteiset taulukot Moniulotteiset taulukot Dynaamiset taulukot. Binääritiedostot. Luento

Ohjelmoinnin peruskurssi Y1

Harjoitus 5 (viikko 48)

C-kieli mahdollistaa hyvin tiiviin ja samalla sekavan tavan esittää asioita, kuitenkin hyvän ohjelman tulisi olla mahdollisimman helppolukuinen ja

Tietorakenteet ja algoritmit

Ohjausrakenteet. Valinta:

Metropolia ammattikorkeakoulu TI00AA : Ohjelmointi Kotitehtävät 3 opettaja: Pasi Ranne

Ohjelmoinnin perusteet, syksy 2006

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

Tietojen syöttäminen ohjelmalle. Tietojen syöttäminen ohjelmalle Scanner-luokan avulla

4. Lausekielinen ohjelmointi 4.1

Ohjelmoinnin perusteet Y Python

Java-kielen perusteet

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4)

Tietotyypit ja operaattorit

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Ohjeita LINDOn ja LINGOn käyttöön

Kääntäjän virheilmoituksia

Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 6: Python

LOAD R1, =2 Sijoitetaan rekisteriin R1 arvo 2. LOAD R1, 100

5. HelloWorld-ohjelma 5.1

Tietuetyypin määrittely toteutetaan C-kielessä struct-rakenteena seuraavalla tavalla:

Luennon sisältö. Taulukot (arrays) (Müldnerin kirjan luku 10) Yksiulotteiset taulukot. Mikä taulukko on? Taulukko-osoitin. tavallinen osoitin

Ohjelmoinnin peruskurssi Y1

C++ Ohjelmoijan käsikirja. Johdanto

Ohjelmoinnin jatkokurssi, kurssikoe

tietueet eri tyyppisiä tietoja saman muuttujan arvoiksi

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Transkriptio:

Tiedosto Monessa tilanteessa olisi hyvä pystyä tallentamaan ohjelman suorituksen aikana syntyvää tietoa pysyvämmin. Nythän kaikki katoaa kun ohjelman suoritus lopetetaan. Tietoja on mahdollista tallentaa tiedostoon. Tiedosto on yhteenkuuluvien tietojen joukko, joka on tallennettu jollekin pitkäaikaistallennukseen pystyvälle medialle, kuten kiintolevylle, CD-levylle tai DVD-levylle. UNIX-järjestelmässä kaikki oheislaitteet ja järjestelmän osat näkyvät ohjelmoijalle tiedostoina. Tiedostot ovat joko binääritiedostoja tai tekstitiedostoja. Tekstitiedostot Tekstitiedosto sisältää ASCII-merkkejä, joista muodostuu peräkkäisiä rivejä, jotka päättyvät rivinvaihtomerkkiin '\n' (newline). Esimerkkinä tekstitiedostosta on lähdekielinen C-ohjelma, HTML-tiedosto jne. Koska tekstitiedostossa on pelkkiä ASCII-merkkejä, kaikki luvut tallennetaan niihin merkkeinä. Esimerkiksi kokonaisluku 345 varaa tekstitiedostosta tilaa kolme merkkiä. Binääritiedostot Binääritiedostot voivat sisältää käytännössä mitä tahansa tietoa: tekstiä, ääntä kuvaa. Binääritiedoston sisältöä ei voida tulkita ASCII-järjestelmän avulla. Esimerkkejä binääritiedostoista ovat C-ohjelmasta käännetty EXE-tiedosto, wordtiedosto, jpg-kuva jne. 91

C-ohjelmien muuttujat voidaan sellaisinaan tallentaa binääritiedostoon, jolloin esimerkiksi kokonaisluku 345 varaa binääritiedostosta tilaa kokonaislukutyypin vaatiman tilanvarauksen verran. Tekstitiedostoja pystyy usein käsittelemään binääritiedostoina, mutta binääritiedostoa ei pysty käsittelemään tekstitiedostona. Tämä johtuu siitä, että tekstitiedostojen yhteydessä suoritetaan automaattisia merkkimuunnoksia. Esimerkiksi, kun tekstitiedostosta tulostetaan kuvaruudulle newline-merkki '\n', se muutetaan kahdeksi ASCII-merkiksi: carriage-return, CR (kursori rivin alkuun) ja line-feed, LF (kursori yksi rivi alaspäin). Binääritiedostoille ei suoriteta missään vaiheessa mitään muunnoksia. Tiedostojen käsittelyn perustoiminnot 1. Tiedoston määrittely Tiedosto määritellään FILE-tietotyypin avulla. FILE on tietue, joka sisältää tiedostonhallintaan liittyviä asioita. Tiedosto määritellään käytännössä määrittelemällä osoitin FILE-tietueeseen: FILE *tiedosto; Tiedosto-osoitin tiedosto on tämän määrittelyn jälkeen jonkin tietovirran (eli tiedoston) nimi. Sitä käytetään kaikissa tiedosto-operaatioissa viittaamaan tiedostoon. On huomattava, että tässä vaiheessa tiedosto ei vielä viittaa mihinkään fyysiseen tiedostoon. 92

2. Tiedoston avaaminen Tiedosto joudutaan avaamaan ennen kuin sitä voidaan käyttää. Avaaminen liittää käytännössä yhteen tiedosto-osoittimen ja fyysisen levytiedoston. Avaamiseen käytetään funktiota fopen(). tiedosto = fopen ("C:\\NIMET.TXT", "r"); fopen() saa kaksi merkkijonoparametria, jotka ovat tiedoston fyysinen nimi ja tiedoston avaustapa. Avaustapa ilmoittaa sen, missä tarkoituksessa tiedostoa käytetään. Kolme avaustapaa ovat: r = tiedostoa luetaan (read) w = tiedostoon kirjoitetaan (write) a = tietojen lisääminen tiedoston loppuun (append). Avaustavan yhteydessä voidaan määritellä, käsitelläänkö tiedostoa teksti- vai binääritiedostona. Oletuksena on tekstitiedosto. Jos halutaan avata tiedosto binäärimuodossa, lisätään lainausmerkkien sisään toiseksi parametriksi b-kirjain, siis rb, wb tai ab. Oheisessa esimerkissä avataan tekstitiedosto kirjoittamista varten ja kirjoitetaan sinne teksti #include <stdio.h> void main(void) FILE *tiedostomuuttuja = fopen("teletapit.txt", "w"); fprintf(tiedostomuuttuja, "teletapit nukkumaan!\n"); fclose(tiedostomuuttuja); 93

Funktio fprintf() toimii samantyylisesti kuin printf(), mutta tulostus tapahtuu tiedostoon, ei näytölle. Jos tiedoston avaaminen onnistuu, fopen() palauttaa osoittimen kyseiseen tiedostoon, muuten se palauttaa arvon NULL. C-ohjelman pääohjelma main() on itse asiassa samanlainen funktio kuin kaikki muutkin funktiot, eli se pystyy palauttamaan arvon käyttöjärjestelmään, josta se käynnistettiin. Siksi oikea tapa määritellä main() on käyttää void:n sijasta sille paluuarvoa int 1. Virhetilanteessa yleinen tapa on, että ohjelman suoritus loppuu siihen ja käyttöjärjestelmälle palautetaan negatiivinen kokonaisluku. Jos ohjelman suoritus menee loppuun ongelmitta, palautetaan nolla tai positiivinen luku. Ohjelman suoritus loppuu ja paluuarvo palautetaan return-lauseella aivan samoin kuin mistä tahansa funktiosta palatessa: #include <stdio.h> int main(void) FILE *avaus; avaus = fopen("oulu.txt", "w"); if(avaus == NULL) printf("tiedoston avauksessa on tapahtunut\ virhe!"); return(-1); fprintf(avaus, "Paskakaupunni"); fclose(avaus); return(0); 1 Modernit C-kääntäjät suorastaan vaativat paluuarvon, toisin kuin paleoliittiselta kaudelta periytyvä Borland 4.5. Uusissa kääntäjissä määrittely void main(void) aiheuttaa virheilmoituksen. Kääntäjät yleensä lisäävät return(0):n automaattisesti main():n loppuun, jos sitä ei siellä ole. 94

Tiedosto oulu.txt tallentuu hakemistoon, joka on C-kääntäjän oletushakemisto ajon aikana. Tiedosto näkyy tiedostolistauksessa ja sen sisältöä voi tutkia jollain tekstieditorilla, vaikkapa Notepadilla: Katsotaan seuraavaksi, miten oulu.txt-tiedostosta luetaan tekstiä: #include <stdio.h> void main(void) FILE *avaus; char puskuri[20]=""; avaus = fopen("oulu.txt", "r"); if(avaus == NULL) printf("tiedoston avauksessa on tapahtunut virhe!"); return(-1); fgets(puskuri,sizeof(puskuri),avaus); printf("%s",puskuri); fclose(avaus); return(0); Nyt tiedot luetaan ensin 20 alkion mittaiseen merkkijonopuskuriin fgets()- funktiolla. fgets() tarvitsee syötteekseen 3 parametria: Puskurin nimi 95

Puskurin koko, joka voidaan määrittää sizeof-operaattorilla. Tiedosto-osoittimen fgets() on turvallisempi vaihtoehto tavalliselle gets()-funktiolle, koska sille voidaan määritellä luettavien merkkien maksimimäärä. Sitä voidaan käyttää lukemaan merkkijonoja myös näppäimistöltä tiedoston sijaan, jolloin tiedoston sijalla parametrilistassa on stdin. 2 fgets jättää rivinvaihtomerkin \n merkkijonon loppuun toisin kuin gets (huom, \n on eri asia kuin merkkijonon lopetusmerkki; molemmat funktiot jättävät loppuun loppumerkin \0 ). Tiedoston sulkeminen Kun tiedoston käsittely lopetetaan, se pitää sulkea funktiolla fclose(), jota käytetään seuraavasti: fclose(tiedosto); missä tiedosto on fopen()-funktion palauttama osoitin. Tavallinen aloittelijan tekemä virhe on unohtaa sulkea tiedosto, ja sitten ihmetellä että miksi mitään ei tallennu mihinkään Harj69 Tee ohjelma, jonka avulla tallennat oman nimesi tiedostoon nimi.txt. Harj70 Tee ohjelma, jonka avulla luet oman nimesi tiedostosta, jonka nimi annetaan ohjelman suorituksen aikana merkkijonomuuttujan avulla. 2 Jo ensimmäinen tunnettu Internet-mato vuodelta 1988, nk. Morris Internet Worm hyödynsi gets-funktion merkkijonopuskurin ylivuotoa. 96

Harj71 Tee ohjelma, joka tulostaa numerot 1-100 tabulaattorilla erotettuna 10 lukua/rivi tiedostoon luvut.txt Harj72 Muokkaa harjoitusta 71, niin että se käy hakemassa luvut tiedostosta ja tulostaa ne viisi kappaletta rivilleen ohjelmaikkunaan. Sijaintiosoitin Kun tiedostoa luetaan tai sinne kirjoitetaan, tiedostojärjestelmän sisäinen muuttuja, sijaintiosoitin (sijaintipointteri), osoittaa aina seuraavan käsittelykohdan. Kun tiedosto avataan, sijaintiosoitin siirtyy automaattisesti tiedoston alkuun. Kun tiedostosta luetaan, sijaintiosoitin siirtyy (jälleen automaattisesti) siihen kohtaan, mihin lukeminen päättyi. Tätä voidaan toistaa, jolloin on kysymys tiedoston peräkkäiskäsittelystä. Peräkkäiskäsittelyssä seuraava lukemistapahtuma aloittaa lukemisen siitä, mihin edellinen jäi. Vastaavasti, kun kirjoitetaan, sijaintipointteri etenee kirjoituksen myötä eteenpäin ja osoittaa aina seuraavan kirjoituskohdan. 97

NICE TO KNOW Tiedosto-osoittimen manipulointiin on C-kielessä tarjolla seuraavia standardikirjaston funktioita: int fseek ( FILE * stream, long int offset, int origin ); o Siirtää tiedosto-osoittimen nykyisestä paikasta origin offset tavua (toimii varmasti oikein vain binääritiedostoille) long int ftell ( FILE * stream ); o Palauttaa tiedosto-osoittimen paikan tavuina tiedoston alusta (toimii varmasti oikein vain binääritiedostoille) void rewind ( FILE * rewind ); o Siirtää osoittimen tiedoston alkuun int fgetpos ( FILE * stream, fpos_t * position ); o Palauttaa osoittimen fpos_t nimiseen stdio.h:ssa määriteltyyn tietueeseen, jonka avulla saadaan tiedosto-osoittimen paikka selville. Tietuetta fpos_t käytetään syötteenä funktiolle: int fsetpos ( FILE * stream, const fpos_t * pos ); o Siirtää osoittimen paikkaan pos. Esimerkki: Sanakirjaohjelma Tehdään seuraavaksi sanakirjaohjelmasta tiedoston avulla toteutettu versio. Sanat on talletettu tekstitiedostoon sanakirja.txt, jonka sisältö näyttää seuraavalta. 98

Kysytään käyttäjältä sana englanniksi ja ohjelma tulostaa vastaavan sanan suomeksi lukemalla se tiedostosta #include <stdio.h> #include <string.h> int main(void) FILE *sanakirjatiedosto; char suomi[20],englanti[20],puskuri[40],sana[20]; printf("anna sana englanniksi\n"); gets(sana); sanakirjatiedosto = fopen("sanakirja.txt", "r"); if(sanakirjatiedosto == NULL) perror("tiedoston avauksessa on tapahtunut virhe!"); return(-1); while (!feof(sanakirjatiedosto)) fgets(puskuri,sizeof(puskuri),sanakirjatiedosto); sscanf(puskuri,"%s %s",&englanti,&suomi); if (strcmp(englanti,sana)==0) printf("suomeksi %s\n",suomi); break; fclose(sanakirjatiedosto); return(0); Ohjelman toiminta lyhyesti: Määritellään 4 kpl merkkijonomuuttujia, joista muuttujaan sana tallennetaan käyttäjän syöttämä sana, jota sanakirjasta etsitään. Yritetään avata sanakirja.txt-tiedosto. Jos avaus ei onnistu, perror()- funktio, joka on erityinen virheilmoitusten tulostukseen tarkoitettu funktio, 99

tulostaa tiedon siitä, mikä oli vialla. Ohjelmasta poistutaan ja palautetaan arvo -1 Jos avaus onnistuu, niin: While-silmukassa luetaan tiedostosta tekstiä rivi kerrallaan niin kauan kuin tiedosto ei ole lopussa. Tiedoston loppuminen tarkastetaan feof()- funktiolla (EOF = End Of File). fgets() suorittaa varsinaisen tekstin lukemisen merkkijonomuuttujaan puskuri. Puskurista luetaan kaksi merkkijonoa muuttujiin englanti ja suomi sscanf()-funktiolla. Verrataan löydettyä englanninkielistä sanaa muuttujaan sana. Jos ovat samat, tulostetaan vastaava suomenkielinen sana. Harj73 Eräs mittauslaite on tuottanut mittausdataa tekstitiedostoon. Insinöörioppilas S.A. Tiaiselle on annettu tehtäväksi kirjoittaa C-ohjelma, joka analysoi tätä dataa. Tiaiselle on kerrottu, tiedostossa on aina yhdellä rivillä juokseva numero sekä kaksoistarkkuuden liukuluku. Auta insinöörioppilasta tekemällä ohjelma, joka tulostaa tiedostossa olevat luvut, niiden lukumäärän, keskiarvon sekä suurimman ja pienimmän arvon. Data löytyy täältä http://www.oamk.fi/~jjauhiai/opetus/lk1/pohjakoodit/data.txt Esimerkki: Tietuetaulukon tallentaminen tekstitiedostoon funktiossa Tässä esimerkissä määritellään yksinkertaisin mahdollinen tietue, joka sisältää yhden kokonaisluvun. Ohjelmassa on funktiot kysy(), tulosta() ja tallenna(). Funktioille viedään osoitin tietuetaulukkoon sekä tietuetaulukon alkioiden määrä, joka kysytään pääohjelmassa. Lisäksi kysytään tiedoston nimi, johon tallennetaan ja viedään tallenna()-funktiolle merkkijonotaulukkona. Tässä 100

esimerkissä on toinen tapa kirjoittaa funktioita. Jos funktioiden määrittelyosat kirjoitetaan ennen main()-funktiota, ei erillisiä prototyyppeja tarvita. #include <stdio.h> struct tietue int arvo; ; void kysy(struct tietue *x,int maara) int i; for (i=0;i<maara;i++) printf("luku?\n"); scanf("%d",&x->arvo); x++; void tulosta(struct tietue *x,int maara) int i; for (i=0;i<maara;i++) printf("%d\n",x->arvo); x++; int tallenna(struct tietue *x,char *tiedosto,int n) int i; FILE *stream; stream=fopen(tiedosto,"w"); if (stream == NULL) perror("virhe : "); return(-1); for (i=0;i<n;i++) fprintf(stream,"%d\n",x->arvo); x++; fclose(stream); 101

return(0); int main(void) tietue luvut[5]; int lkm; char tiedostonimi[30]; printf("mihin tallennetaan?\n"); gets(tiedostonimi); printf("montako lukua?\n"); scanf("%d",&lkm); kysy(luvut,lkm); tulosta(luvut,lkm); tallenna(luvut,tiedostonimi,lkm); return(0); Harj74 Tee Harjoituksen 67 opiskelijarekisteriohjelmasta versio, joka tallettaa syötetyt tiedot tiedostoon. Kirjoita tallennus omaksi funktiokseen. Harj75 Lisää edelliseen harjoitustehtävään funktio, joka lukee tiedostosta opiskelijarekisterissä olevat tiedot ja tulostaa ne näytölle. Harj76 Tee edellisen harjoitustehtävän ohjelmaan tekstipohjainen valikko, jossa on seuraavat toiminnot: o Lisää opiskelija rekisteriin o Hae rekisterissä olevat opiskelijat o Tulosta opiskelijarekisterin sisältö o Tallenna opiskelijarekisterin sisältö o Lopeta 102

Binääritiedostoon kirjoittaminen ja lukeminen, hex-editori Binääritiedoston sisältöä ei voi tutkia tekstieditorilla. On kuitenkin erityisiä ohjelmia, ns. hex-editoreita, joiden avulla voidaan tutkia binääritiedostojen sisältöä. Suurempia datamääriä tallennettaessa binääriformaatti on huomattavasti tehokkaampi tapa tallentaa tietoja kuin tekstiformaatti. Binääritiedosto vie vähemmän levytilaa ja sekä sen lukeminen että sinne kirjoittaminen on nopeampaa. Ohjelma tallentaa 1000 kokonaislukua 0 999 binääritiedostoon. Erona tekstitiedostoon kirjoittamiseen on, että luvut kirjoitetaan yhdellä kertaa fwrite-funktiolla. #include <stdio.h> const int N=1000; int main(void) FILE *avaus; avaus = fopen("numeroita.bin", "wb"); int i,taulukko[n]; if(avaus == NULL) printf("tiedoston avauksessa on tapahtunut virhe!"); return(-1); for (i=0;i<n;i++) taulukko[i]=i; fwrite(taulukko,sizeof(int),\ sizeof(taulukko)/sizeof(int),avaus); fclose(avaus); return(0); 103

Ohjelmasta kannattaa huomata, että tiedot kirjoitetaan levylle for-silmukan jälkeen kertalinttuulla. Funktiolla fwrite() annetaan parametrina: o Tulostettava data, tässä osoitin kokonaislukutaulukon alkuun o Tallennettavan dataelementin koko tavuina. Yleensä on turvallisinta varata elementille tilaa kyseisen tietotyypin maksimikoon verran. Maksimikoko voidaan selvittää sizeof()-funktion avulla. Eli tässä esimerkissä sizeof(int) laskee kokonaisluvun tavujen lukumäärän tässä järjestelmässä (4). Sillä varmistetaan, että suurin mahdollinen kokonaisluku pystytään tallentamaan. o Kolmas parametri ilmoittaa tallennettavien alkioiden lukumäärän. Jotta lukumäärä tulee varmasti oikein kaikissa tapauksissa, on tämä luku syytä laskea kaavalla sizeof(taulukko)/sizeof(int) (tässä 4000/4 = 1000). o Viimeisenä annetaan tiedosto-osoitin. Jos käytettäisiin tekstiformaattia, pitäisi jokainen luku kirjoittaa erikseen silmukan sisällä fprintf:llä, joka on huomattavan paljon hitaampaa. Jos numeroita.bin-tiedoston avaa Notepadilla, tulostus näyttää seuraavalta: Tämä ei selvästikään näytä siltä, että tiedostossa olisi tallennettuna 1000 kokonaislukua. Jos sen sijaan käytetään hex-editoria, joka ei kuulu Window$in 104

vakiovarustukseen, mutta joita löytyy netistä, näyttää tiedoston sisältö seuraavalta: Yksi tavu vastaa yhtä kahden numeron kokoista heksalukua keskimmäisessä kentässä (00 00 00 00 01 00 00 00 ). Ohjelmassa oli määritelty, että jokainen luku tallennetaan 4 tavulla. Siten jokaista tallennettua lukua vastaa neljän heksaluvun ryhmä: 00 00 00 00 = 0 01 00 00 00 = 1 02 00 00 00 = 2 03 00 00 00 = 3 Jne.. Luvut on esitetty ns. big endian -muodossa, jossa eniten merkitsevä tavu on viimeisenä. Esimerkki: Binääritiedoston lukeminen Edellä talletettu tiedosto voidaan lukea fread()-funktiolla. Parametrit ovat samat kuin fwrite():llä. 105

#include <stdio.h> const int N=1000; int main(void) FILE *avaus; avaus = fopen("numeroita.bin", "rb"); int i,taulukko[1000]=; if(avaus == NULL) printf("tiedoston avauksessa on tapahtunut virhe!"); return (-1); fread(taulukko,sizeof(int),\ sizeof(taulukko)/sizeof(int),avaus); for (i=0;i<1000;i++) printf("%d ",taulukko[i]); fclose(avaus); return(0); Esimerkki: Tyypillisen mittauslaitteen dataformaatin tuottaminen ja lukeminen Monet lääketieteelliset mittauslaitteet (esim. EEG tai EKG-laite) tallentavat mittaustuloksensa yleensä binääritiedostoon, vaikka XML-formaatti (joka on tekstiformaatti) onkin viime aikoina yleistynyt. Alan insinöörin olisi varmaankin hyvä tietää, miten datan saa luettua omaan ohjelmaan jatkokäsittelyä varten. Tiedosto koostuu tyypillisesti kahdesta osasta: o Header sisältää mittauksen tiedot, kuten potilaan nimen ja tunnisteen (ID), mittauspäivämäärän ja laitteen parametreja, sekä paljon muuta, laitekohtaista tietoa. o Data sisältää itse mittausdatan jossain muodossa, yleensä kokonaislukuina. 106

Tiedoston rakenne on valitettavasti monesti laite- tai valmistajakohtainen, joskin tiedostoformaatteja on viime vuosin pyritty standardoimaan 3. Tiedoston tarkka rakenne on selvitettävä. Yleensä laitevalmistajat antavat tämän tiedon kysyttäessä. Seuraavassa ohjelmassa kirjoitetaan yksinkertainen itse keksitty mittaustiedosto ja luetaan se toisessa ohjelmassa. #include <stdio.h> int main(void) FILE *avaus; char patname[30]="pelle Peloton"; char date[10]="23.3.2007"; char id[10]="abc666"; int data[1000]=; //Dataksi pelkkiä nollia avaus = fopen("testi.bin", "wb"); int i,taulukko[1000]=; if(avaus == NULL) perror("tiedoston avauksessa on tapahtunut virhe!"); return (-1); fwrite(patname,sizeof(char),sizeof(patname)/\ sizeof(char),avaus); fwrite(date,sizeof(char),sizeof(date)/\ sizeof(char),avaus); fwrite(id,sizeof(char),sizeof(id)/sizeof(char),avaus); fwrite(data,sizeof(int),sizeof(data)/\ sizeof(int),avaus); fclose(avaus); return(0); Tiedostoa luettaessa on tietotyyppien vastattava tavun tarkkuudella toisiaan: 3 Eräs biosignaalien standardi on EDF, eli European Data Format, josta löytyy lisätietoa oheisesta linkistä:: http://www.edfplus.info/specs/index.html 107

#include <stdio.h> int main(void) FILE *avaus; char patname[30]; char date[10]; char id[10]; int data[1000]; avaus = fopen("testi.bin", "rb"); int i,taulukko[1000]=; if(avaus == NULL) perror("tiedoston avauksessa on tapahtunut virhe!"); return (-1); fread(patname,sizeof(char),sizeof(patname)/\ sizeof(char),avaus); fread(date,sizeof(char),sizeof(date)/\ sizeof(char),avaus); fread(id,sizeof(char),sizeof(id)/sizeof(char),avaus); fread(data,sizeof(int),sizeof(data)/sizeof(int),avaus); printf("%s\n%s\n%s\n",patname,date,id); for (i=0;i<1000;i++) printf("%d ",data[i]); fclose(avaus); return(0); Harj77 Tee ohjelma, joka tallentaa luvut 0-999 binääritiedostoon, lukee ne sieltä ja tulostaa näytölle. Harj78 Tallenna vapaasti valittava teksti binäärimuodossa tiedostoon ja lähetä se kaverille sellaisten tietojen kanssa että hän pystyy sen avaamaan. Harj79 Muuta harjoituksen 76 opiskelijarekisteriä niin, että tiedot tallentuvat binääritiedostoon. 108

Komentoriviargumentit Main-funktion sielunelämään liittyy vielä yksi tähän mennessä käsittelemätön juttu. Joku on ehkä joskus käyttänyt tietokonetta MS-DOS-aikakaudella ennen Windowsia, tai käyttänyt Unix- tai Linux-käyttöjärjestelmiä. Näissä järjestelmissä ohjelmia voidaan käynnistää ns. komentotulkista (command interpreter). Itse asiassa Window$:n ikonin klilkkaaminen on sama asia kuin että vastaava komento kirjoitetaan komentotulkkiin. C-kieltä on käytetty paljon esimerkiksi käyttöjärjestelmien ohjelmointiin. Niinpä varsinkin Unixin ja C-kielen kehitys liittyvät läheisesti toisiinsa. C-kielen main()-funktion täydellinen muoto on int main(int argc,char *argv[]) Toisin sanoen main():lle voidaan välittää tietoa käyttöjärjestelmästä: o argc = argument count, eli kuinka monta parametria main():lle syötetään o argv = osoitin merkkijonotaulukkoon, joka sisältää itse komentoriviparametrit. Ensimmäinen komentoriviargumentti argv[0] on itse suoritettavan komennon nimi. #include <stdio.h> int main (int argc, char *argv[]) int count; printf ("1. argumentti %s\n",argv[0]); 109

if (argc > 1) printf("muut argumentit:\n"); for (count = 1; count < argc; count++) printf("argv[%d] = %s\n", count, argv[count]); else printf("komennolla ei ollut argumentteja.\n"); return 0; Toisessa esimerkissä tehdään yhteenlaskuohjelma summa(), jota kutsutaan komentoriviltä: #include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) int n; double eka=0,toka=0; 110

if (argc!=4) printf("virhe!\n"); return(-1); eka=atof(argv[1]); toka=atof(argv[3]); if (eka == 0 toka == 0) printf("virhe"); return(-1); printf("%f\n",eka+toka); return 0; Komentoriviltä suoritettavaksi tarkoitettu käännetään build -komennolla, ei normaalilla compile/run-systeemillä. Build tekee suoritettavan exe-tiedoston, joka ajetaan Window$in Command Promptissa (Komentokehotteessa). Harj80 Tee komentoriviltä suoritettava nelilaskin, jolle annetaan kaksi lukua ja niiden välissä merkki +, -, * tai /. 111

Ohjelman jakaminen useampaan tiedostoon Opintojakson viimeisenä asiana käsitellään C-ohjelman jakamista useaan tiedostoon. Tämä on käytännössä välttämätöntä, kun ohjelman koko kasvaa suureksi. Ohjelmaan on helppo lisätä uusia ominaisuuksia ja se pysyy näin helpommin hallittavana. Suurissa ohjelmissa saattaa olla satoja C-kielisiä tiedostoja ja satojatuhansia rivejä koodia. Lienee selvää, ettei kukaan pystyisi sellaista ohjelmaa ymmärtämään, jos koko koodi olisi yhdessä pötkössä. Tässä käytetään esimerkkinä Dev-C++ ympäristöä. Muissa Windows-kääntäjissä pitäisi löytyä suurin piirtein vastaavat toiminnot, joskin mahdollisesti hieman eri nimisinä. Unix-ympäristössä operoidaan ns. makefile:n avulla, jonne listataan käännettävät tiedostot ja se suoritetaan make-nimisen ohjelman avulla. Ohjelman jakaminen aloitetaan avaamalla uusi projektitiedosto: File -> New -> Project 112

Vaihdetaan Project options:ssa vaihtoehdoksi C Project ja tyypiksi Console Application. Seuraavassa esimerkissä tehdään merkkijonon merkkien laskentaohjelma, joka on jaettu kolmeen tiedostoon: o main.c sisältää varsinaisen pääohjelman o laskemerkit.h on käyttäjän itse määrittelemä header- eli otsikkotiedosto, joka sisältää tässä esimerkissä funktion laskemerkit() prototyypin. o laskemerkit.c sisältää varsinaisen funktion laskemerkit() rungon eli määrittelyosan. Nämä tiedostot voidaan lisätä projektiin File -> New source file - valikon kautta. Dev-C++:n käyttöliittymä näyttää nyt tältä. Eli Project2:n alle on lisätty kolme edellä mainittua tiedostoa. Kuvassa näkyy pääohjelman sisältö. Huomaa, että 113

oma otsikkotiedosto laskemerkit.h on sisällytettävä otsikkotiedostoihin. Sitä ei merkitä hakasulkuihin, vaan lainausmerkkeihin. Tiedosto laskemerkit.h sisältää vain yhden rivin: int laskemerkit(char *); Ja tiedosto laskemerkit.c seuraavan koodin: int laskemerkit(char *s) int i=0; while (s[i]!='\0') i++; return i; Yhteen omaan otsikkotiedostoon voidaan kirjoittaa useamman funktion prototyypit. Jokainen funktio on kuitenkin kirjoittaa omiin tiedostoihinsa, jolloin koodin ylläpidettävyys säilyy hyvänä. Harj81 114

Muuta nelilaskinohjelmaa siten, että kukin laskutoimitus on kirjoitettu omaan tiedostoonsa. Kaikkien neljän funktion prototyypit on kirjoitettu yhteen otsikkotiedostoon. Harj82 Lisää edellä esitettyyn esimerkkiohjelmaan omaan tiedostoonsa funktio int laskekirjain(char *merkkijono,char haettava_merkki); joka laskee syötetystä merkkijonosta, kuinka monta kertaa valittu kirjain esiintyy. 115

Johdatus ohjelmointiin-opintojakso päättyy tähän. Syksyllä jatketaan Olio-ohjelmoinnilla. Kaikkia kielen piirteitä ei vielä tässäkään ajassa pystytty kattamaan, mutta tärkeimmät perusasiat kuitenkin. Onnea viimeiseen välikokeeseen ja aurinkoista kesää. Jukka & Johanna. Ode to C stumpf@gtenmc.gtetele.com (Jon S. Stumpf) GTE Telecom Inc., Bothell, WA (computer, chuckle) 0x0d2C May your signals all trap May your references be bounded All memory aligned Floats to ints rounded Remember... Non-zero is true ++ adds one Arrays start with zero and, NULL is for none For octal, use zero 0x means hex = will set == means test use -> for a pointer a dot if its not? : is confusing use them a lot a.out is your program there's no U in foobar and, char (*(*x())[])() is a function returning a pointer to an array of pointers to functions returning char 116

117