91 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, word- tiedosto, jpg-kuva jne.
27
Embed
Tiedosto - Oamkjjauhiai/opetus/LK1/C5.pdf · 2007-03-26 · 93 2. Tiedoston avaaminen Tiedosto joudutaan avaamaan ennen kuin sitä voidaan käyttää. Avaaminen liittää käytännössä
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
91
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, word-
tiedosto, jpg-kuva jne.
92
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.
93
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
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 int1.
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.
95
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
96
• 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.
97
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.
98
Esimerkki: Sanakirjaohjelma
Tehdään seuraavaksi sanakirjaohjelmasta tiedoston avulla toteutettu versio.
Sanat on talletettu tekstitiedostoon sanakirja.txt, jonka sisältö näyttää
seuraavalta.
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