TIEDOSTOJEN KÄSITTELY

Tietokoneresurssien olennainen osa ovat tiedostot. Tiedostojen käsittelyä tehdään kaikentyyppisissä sovelluksissa. Tiedostoja käytetään ohjelmien, asiakirjojen ja kaikentyyppisen tiedon tallentamiseen. Ohjelmoija joutuu siksi kirjoittamaan ohjelmia joissa luodaan tiedostoja, kirjoitetaan tai luetaan tietoa tiedostoista. Tiedostojen käsittelyä on kahta eri muotoa: peräkkäis- ja suorasaantityyppistä. Peräkkäissaantityyppisessä käsittelyssä ohjelman on luettava tiedostoa sen alusta alkaen voidakseen lukea tietoa myös tiedoston lopusta. Tätä voi verrata kasettinauhan toimintaan, jossa on käytävä läpi nauha alusta alkaen läpi jotta saadaan esille nauhan loppuosan tiedot. Suorasaantikäsittely mahdollistaa tietyn paikan hakemisen tiedostosta ilman, että tarvitsee lukea aikaisempaa tietoa. Suorasaantikäsittelyssä I/O-toiminnot ovat verrattavissa CD-levyn käyttämiselle. CD-levyltä voi valita minkä tahansa kohdan joutumatta käymään läpi edellisiä tietoja.


TIEDOSTO-OSOITIN

Tietotyyppi FILE käsitetään yleisesti tiedoston osoittimena. FILE on todellisuudessa osoitin tietoon, ja se määrittelee tiedoston lukuisia ominaisuuksia kuten tiedoston nimen, tilan ja tämän hetkisen siirtymäaseman. Tiedoston osoitin edustaa tiettyä levytiedostoa. Tiedoston osoitinta käytetään siihen yhdistetyn virran (vuon) toimesta ohjaamaan syöttö- ja tulostusfunktioiden toimintaa.

Tiedosto-osoittimen määrittely

Jotta tiedosto-osoitin saadaan liitettyä ohjelmaan on tehtävä seuraava määrittely:

FILE *fptr;


TIEDOSTON AVAAMINEN

Ennen kuin levytiedostoa voidaan lukea tai siihen voidaan kirjoittaa on tiedosto avattava. Tiedoston avaaminen aikaansaa yhteisymmärryksen ohjelman ja käyttöjärjestelmän välille siitä, mitä tiedostoa tullaan käsittelemään ja miten käsittely tapahtuu.>br> Tiedoston avaamiseen käytetään funktiota fopen(). Funktio saa kaksi parametria ja palauttaa FILE tyyppisen muuttujan.

Funktion fopen() määrittely:

FILE *fopen(const char tiedostonimi, const char ”moodi);


Lauseessa tunniste tiedostonimi on merkkijono, joka kuvaa tiedoston nimeä. Tiedoston nimi voi sisältää myös polkumäärityksen. Parametri moodi määrittää, kuinka tiedosto avataan. Se on merkkijono, joten sen ympärille on laitettava aina lainausmerkit.

Tiedostomoodin tunnisteet:

merkkijono tarkoitus r Avaa tiedosto vain lukemista varten. Tiedoston on oltava olemassa. w Luo tiedosto kirjoittamista varten. Mikäli ky- seinen tiedosto on jo olemassa, sen päälle kir- joitetaan. a Lisää, avaa tiedosto loppuun kirjoittamista var- ten tai luo tiedosto kirjoitamista varten, ellei tiedostoa ole olemassa. r+ Avaa olemassa oleva tiedosto päivittämistä var- ten (lukeminen tai kirjoittaminen). w+ Luo tiedosto päivitystä varten (lukeminen tai kirjoittaminen). Jos kyseinen tiedosto on ole- massa, sen päälle kirjoitetaan. a+ Avaa tiedosto lisäämistä varten, tiedoston lop- puun päivittämistä varten tai luo tiedosto, el- lei sitä ole olemassa.

Esim.

FILE *fileptr; fileptr = fopen(”CONFIG.SYS”, ”r”);

Funktio fopen() palauttaa osoittimen tiedostoon. Mikäli tiedostoa avattaessa tapahtuu virhe, funktio palauttaa NULL -osoittimen.

Haluttaessa varmistua siitä, että tiedoston avaus onnistui, voidaan käyttää seuraavaa koodijaksoa.

FILE *fptr; if (( ftpr = fopen(”C:\\TEST.DAT”, ”r”)) == NULL ) { printf(”Virhe: Tiedoston avaaminen ei onnistunut\n”); exit (0); }


I/O -FUNKTIOIDEN KÄYTTÄMINEN MERKKITIEDOSTOJEN YHTEYDESSÄ

Tiedoston avaamisen jälkeen siihen voidaan kirjoittaa tietoa tai siitä voidaan lukea tietoa. Yksinkertaisimmat keinot tähän tarkoitukseen ovat funktioiden fgetc() ja fputc() käyttö. Funktiot toimivat paljolti samoin kuin funktiot getchar() ja putchar(). Erona on, että funktioille on kerrottava mitä tiedostoa ne käyttävät. Tiedosto määritetään välittämällä funktiolta fopen() saatu tiedosto-osoitin funktiolle.

Merkkien lukeminen:
Esimerkkiohjelma matkii DOS:n type-komentoa. Ohjelmalle annetaan tiedoston nimi jonka sisältö sitten tulostetaan näytölle.

Esim.

/*tekstitiedostojen avaamiseen joiden pääte on esim. txt, c tai cpp. Jos koodi1.c -tiedosto sijaitsee esim F-asemassa Teksti-kansiossa, kirjoita "Anna tiedoston nimi" vastaukseksi seuraava: F:/Teksti/koodi1.c ja paina Enter */ #include <stdio.h> #include <stdlib.h> int main() { char ch, filename[85]; FILE *fileptr; printf("Avattava tekstitiedosto voi olla esim txt-päätteinen.\n"); printf("Avaa tietokoneellesi tai verkkoasemaan talletettu tiedosto.\n\n"); printf("Anna tiedoston sijainti ja tiedoston nimi\n"); gets(filename); if ((fileptr = fopen(filename, "r")) == NULL) { printf("\nTiedoston avaus ei onistunut"); sleep(2000); exit (0); } printf("Tiedoston %s tulostus\n\n"A, filename); while (!feof (fileptr)) { ch = fgetc(fileptr); putchar(ch); } fclose(fileptr); getch(); return 0; }




Ohjelmassa luetaan tiedostosta fgetc()-funktiolla merkki kerrallaan ja tulostetaan näytölle merkeittäin funktiolla putchar().

while-silmukassa käytetään funktiota feof() etsimään tiedoston loppumerkkiä (EOF). Tiedoston loppumerkki on erikoismerkki, jonka käyttöjärjestelmä asettaa kunkin tiedoston loppun. Lopumerkin tehtävänä on kertoa käyttöjärjestelmälle tiedoston loppumisesta. Tästä syystä esimerkissä voidaan jatkaa merkkien lukemista niin kauan kunnes saavutetaan tiedoston loppu. Kun funktion feof() arvo tulee todeksi, silmukasta poistutaan ja tiedosto suljetaan. Tiedoston sulkeminen tapahtuu funktion fclose() avulla. Funktio sulkee tiedoston, joka tunnistetaan tiedoston osoitinparametrin avulla. Funktio palauttaa arvon, joka ilmaisee, pystyttiinkö tiedosto sulkemaan. Esimerkkiohjelmassa ei tuota arvoa tarkastettu erikseen.

Merkkien kirjoittaminen:
Kirjoittaminen tiedostoon merkki kerrallaan tapahtuu funktion fputc() avulla. Funktion fputc() yleinen muoto:

int fputc(int c, FILE *fileptr);

Arvo on kokonaislukuvakio, joka määrittää, mikä merkki kirjoitetaan tiedostoon. Tunniste fileptr osoittaa FILE-muuttujaan, joka avaa tiedoston.

FILE *fptr; fptr = fopen(”FILE.TXT”, ”w”); fputc(”p”, fptr); fclose(fptr);

Esim.

/* Ohjelma kirjoittaa merkkejä tiedostoon */ #include <stdio.h> #include <stdlib.h> #include <process.h> int main() { char ch, filename[85]; FILE *fileptr; printf(”Anna tiedoston nimi\n”); gets(filename); printf(”Lopetus merkillä # tyhjälle riville\n”); if ((fileptr = fopen(filename, ”w”)) == NULL) { printf(”Tiedoston avaaminen ei onnistunut\n”); exit (0); } while (( ch = getchar()) != ‘#’) fputc(ch, fileptr); fclose(fileptr); return 0; }




Ohjelma sijottaa silmukassa jokaisen kirjoitetun merkin tiedostoon kunnes syötetään merkki #. Tämän jälkeen tiedosto suljetaan ja ohjelma päättyy. Jokainen merkinpainallus ei aiheuta kirjoittamista levylle vaan merkit kirjoitetaan ensin puskuriin. Puskuri kirjoitetaan levylle sen tultua täyteen tai kun tiedosto suljetaan funktiolla fclose().


I/O -FUNKTIOIDEN KÄYTTÖ MERKKIJONOJEN KANSSA

C:n kirjastoissa on funktioita, joiden avulla voidaan lukea tai kirjoittaa merkkijonoja yhdellä kertaa. Tähän tarkoitukseen käytetään funktioita fputs() ja fgets(). Funktiot muistuttavat funktioita puts() ja gets().

Esim.

#include <stdio.h> #include <stdio.h> #include <process.h> #define MAXLINELEN 135 int main() { char filename[85], strline[MAXLINELEN]; int line = 0; FILE *fileptr; printf(”Anna tiedostonimi\n”); gets(filename); if ((fileptr = fopen(filename, ”r”)) == NULL) { printf(”Tiedosto ei aukea\n”); exit (0); } while (!feof(fileptr)) { fgets(strline, MAXLINELEN, fileptr); printf(”%s”, strline); line++; } fclose(fileptr); printf(”\n%d riviä oli tiedostossa\n”, line); return 0; }




Ohjelmassa käytetään funktiota fgets(). Funktio käyttää kolmea parametria: merkkitaulukko, johon merkkijono tallennetaan. Seuraava parametri on merkkijonoon luettavien merkkien maksimipituus. Tämä parametri estää funktiota lukemasta liian pitkää merkkijonoa ja samalla ylittämästä taulukon rajoja. Kolmas parametri on tiedosto-osoitin, joka kertoo funktiolle mitä tiedostoa käsitellään.

fgets(strline, MAXLINELEN, fileptr);

Esim.

/* Ohjelma kirjoittaa merkkijonoja levytiedostoon */ #include <stdio.h> #include <stdio.h> #include <string.h> #define MAXLINELEN 135 int main() { char filename[85], strline[MAXLINELEN]; FILE *fileptr; printf(”Lopetus: Enter tyhjällä rivillä\n”); printf(”Anna tiedostonimi\n”); gets(filename); if ((fileptr = fopen(filename, ”w”)) == NULL) { printf(”Tiedosto ei aukea\n”); exit (0); } while (strlen(gets(strline)) > 0) { fputs(strline, fileptr); fputs(”\n”, fileptr); } fclose(fileptr); return 0; }



Ohjelman while-silmukka tarkastaa merkkijonon pituuden. Mikäli merkkijonon pituus on nolla silmukka päättyy. Muutoin funktio fputs() tulostaa merkkijonon avoimeen tiedostoon. Funktio fputs() ei suorita rivinvaihtoa vaan tämä on tehtävä omalla lauseella.


ERIMUOTOISTEN TIETOJEN SYÖTTÖ / TULOSTUS

Muotoilevan syötön ja tulostuksen avulla tiedostoja voi käsitellä samanlaisina syötön ja tulostuksen kohteina kuin merkkijonoja funktioiden printf() ja scanf() avulla. Käytettävät funktiot operoivat levytiedostojen kanssa ja käyttävät FILE-osoitinta ensimmäisenä parametrinaan.

Funktiot fprintf() ja fscanf()

int fprintf(FILE *stream, const char *format [, argumentti, ...]); int fscanf(FILE *stream, const char *format [, osoite, ...]);

Esim.

fprintf(fptr, ”Muuttujan arvo on %d”, a + b); fscanf(fptr, ”%d”,x);

Esim. Ohjelmaesimerkki fprintf().

#include <stdio.h> #include <process.h> #include <conio.h> #include <string.h> #include <ctype.h> int main() { char cont[2]; char name[25]; int age; FILE *fileptr; if ((fileptr = fopen(”NAMES.DAT”,”w”)) == NULL) { printf(”Tiedostoa ei voi avata”); exit (0); } strcpy(cont, ”Y”); while (strcmp(”Y”, cont) == 0) { printf(”Anna nimi:\n); scanf(”%s”, name); printf(”Anna ikä\n”); scanf(”%d”,&age); fprintf(fileptr, ”%s %d\n”, name, age); printf(”Annatko toisen nimen (K / E)\n”); scanf(”%s”,cont); cont[0] = toupper(cont[0]); } fclose(fileptr); return 0; }

Ohjelmassa fprintf()-funktio saa yhden parametrin enemmän kuin tavallinen printf()-funktio. Lisäparametri on tiedosto-osoitin, joka viittaa tiedostoon johon kirjoitetaan. Muotoilumääreet ovat samat kuin printf()-funktiossa.

Esim. Ohjelmaesimerkki fsanf().

#include <stdio.h> #include <process.h> #include <stdlib.h> int main() { char buffer[25]; int number; FILE *fileptr; if ((fileptr = fopen(”NAMES.DAT”,”r”)) == NULL) { printf(”Tiedostoa ei voi avata”); exit (0); } while (!feof(fileptr)) { fsanf(fileptr, ”%s”, buffer); printf(”Nimi = %s, ”, buffer); fsanf(fileptr, ”%d”, &number); printf(”Ikä on %d\n”, number); } fclose(fileptr); return 0; }







TIEDOSTOKOHTAISET SUORASAANTITOIMINNOT I/O

Suorasaantityyppiset tiedoston syöttö- ja tulostustoiminnot mahdollistavat käsitellä suoraan mitä tahansa osaa tiedostosta tarvitsematta ensin lukea tiedoston aikaisempia osia. Suorasaantitoiminnoille tiedostot avataan tavallisesti binäärisessä muodossa, koska tiedon tallentaminen tapahtuu binäärisenä nopeammin ja C-rutiinien on myös helpompi lukea ja kirjoittaa binääritietoa. Binäärimuotoinen tieto tarvitsee myös vähemmän levytilaa kuin tekstimuotoinen tieto.

Esim. Näyttää tiedoston käänteisenä.

#include <stdio.h> #include <process.h> int main() { char ch, filename[85]; FILE *fileptr; long lastpos; printf(”Anna tiedoston nimi\”); gets(filename); if ((fileptr = fopen(filename, ”r”)) == NULL) { printf(”Tiedostoa ei voi avata”); exit (0); } fseek(fileptr, 0, SEEK_END); lastpos = ftell(fileptr); while(!feof(fileptr)) { fseek(fileptr, --lastpos, SEEK_SET); ch = fgetc(fileptr); putchar(ch); } fclose(fileptr); return 0; }




Ohjelma soveltuu käytettäväksi ASCII-tiedoston kanssa.

Ohjelmassa on kaksi funktiota fseek() ja ftell(). fseek() mahdollistaa suurtymisen mihin kohtaan tahansa tiedostossa. Funktio ftell() palauttaa long tyyppisen tiedon, joka kertoo nykyisen sijaintikohdan tiedostossa. Todellisuudessa funktio palauttaa tavujen määrän tiedoston alusta alkaen ja ensimmäinen tavu on numeroitu tavuksi nolla. Ohjelmassa käytetään funktiota fseek() siirtymisessä tiedoston loppuun (SEEK_END), jonka jälkeen funktio ftell() palauttaa sen hetkisen sijaintikohdan tiedostossa. Sijaintikohta talletetaan muuttujan lastpos.

Funktion fseek() yleinen muoto

int fseek(FILE *stream, long offset, int whence);

Funktio käyttää kolmea parametria, joista ensimmäinen osoittaa käsiteltävään tiedostoon. Seuraava argumentti, offset, on long-tyyppinen ja se ilmaisee, kuinka pitkälle lähtökohdasta on siirryttävä. Siirtymä voi olla positiivinen, jolloin siirrytään eteenpäin tai negatiivinen, jolloin siirrytään taaksepäin. Mikäli parametri offset on nolla mitään siirtymää ei tapahdu. Kolmas argumentti on nimeltään whence, joka esittää alkupisteen josta siirtymä lasketaan. Funktio fseek palauttaa nollan, jos siirtyminen onnistuu ja nollasta poikkeavan arvon, jos tiettyyn kohtaan siirtyminen ei onnistunut.

Vakiot parametrille whence:

Vakion nimi Arvo Alkupiste SEEK_SET 0 Tiedoston alku SEEK_CUR 1 Nykyinen sijainti SEEK_END 2 Tiedoston loppu


TIETUEIDEN KIRJOITTAMINEN JA LUKEMINEN

Esimerkkiohjelmassa on yksinkertainen tietue, jossa on kaksi kenttää. Ohjelma pyytää käyttäjää antamaan tietoa tietueen kenttiin, jonka jälkeen tiedot tallennetaan levylle.

Esim. Ohjelma kirjoittaa tietuemuotoista tietoa levylle.

#include <stdio.h> #include <string.h> #include <process.h> #include <ctype.h> struct inforec { char name[85]; int age; }; int main() { char ch = ‘N’; char filename[85]; FILE *fileptr; struct inforec person; printf(”Anna tiedoston nimi: \n”); gets(filename); if ((fileptr = fopen(filename, ”w”)) == NULL) { printf(”Tiedostoa ei voi avata”); exit (0); } do { printf(”Anna nimi\n”); scanf(”%s”;person.name); printf(”Anna ikä\n”); scanf(”%d”, &person.age); fwrite(&person, sizeof(person), 1, fileptr); fflush(stdin); printf(”Lisäätkö nimen tiedostoon (K/E)\n”); ch = getchar(); } while (toupper(ch) == ‘K’); fclose(fileptr); printf(”\nTiedoston %s tallennus OK\n”, filename); return 0; }




Esimerkkiohjelmassa on funktio fwrite(). Funktio käyttää neljää argumenttia, joista ensimmäinen on puskurin sijaintipaikka, johon tieto talletetaan. Toinen argumentti määrittää, kuinka monta tavua kirjoitetaan. Kolmas argumentti määrittää, kuinka monta tietuetta kirjoitetaan. Viimeinen argumentti on FILE-osoitin.
Mikäli funktio palauttaa arvon yksi on kirjoittaminen onnistunut. Nolla paluuarvona merkitsee kirjoittamisen epäonnistumista.

size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream); FILE *fptr; char str[10]; fwrite(&str, sizeof(str), 1, fptr);

Tietueiden lukeminen

Funktion fwrite() vastapuoli on funktio fread(). Funktio lukee tietyn määrän tavuja ja tallentaa tiedot muistipaikkaan.

size_t fread(void *ptr, size_t size, size_t n, FILE *stream);

Ensimmäinen parametri on osoite puskuriin, johon luettu tieto talletetaan. Toinen parametri kertoo, kuinka monta tavua kuhunkin tietueeseen luetaan. Kolmas parametri ilmaisee, kuinka monta tietuetta luetaan. Viimeinen parametri on FILE-osoitin avoimeen tiedostoon.

FILE *fptr; char str[10]; fread(&str, sizeof(str), 1, fptr);

Esim. Ohjelma lukee tietueita levytiedostosta.

#include <stdio.h> #include <stdlib.h> #include <process.h> struct inforec { char name[85]; int age; }; int main() { char ch, filename[85]; FILE *fileptr; int recnumb = 1; struct inforec person; printf(”Anna Tiedoston nimi\n”); gets(filename); if ((fileptr = fopen(filename,”r”)) == NULL) { printf(”Tiedostoa ei voi avata”); exit (0); } while(fread(&person, sizeof(person), 1, fileptr) == 1) { printf(”Tietue # %d\n”, recnumb++); printf(”Nimi on %s\n”, person.name); printf(”Ikä on %d\n”, person.age); printf(”Paina Enteriä siirtyäksesi seuraavaan tietueeseen\n”); ch = getchar(); } fclose(fileptr); printf(”Tiedoston loppu”); return 0; }




TAULUKOIDEN KIRJOITTAMINEN JA LUKEMINEN

Samalla tavalla kuin käytetään fread()- ja fwrite()-funktiota tietueiden lukemiseen ja kirjoittamiseen, voidaan lukea ja kirjoittaa mitä tahansa tietotyyppiä.

Esim. Ohjelma lukee ja kirjoittaa taulukon.

#include <stdio.h> #include <stdlib.h> #include <process.h>> #define ITEMS 7 int main() { char filename[85]; int count; FILE *fileptr; int data[ITEMS] = { 8, 57, 5, 309, 33, 87,55}; int data[ITEMS]; printf(”Anna tiedoston nimi\n”); gets(filename); /* kirjoitetaan taulukko tiedostoon */ if ((fileptr = fopen(filename,”w”)) == NULL) { printf(”Tiedostoa ei voi avata”); exit (0); } printf(”Kirjoitetaan tietoalkioita tiedostoon %s\n”, filename); fwrite(data, sizeof(data), 1, fileptr); fclose(fileptr); /*luetaan tiedosto taulukkoon */ if ((fileptr = fopen(filename, ”r”) == NULL) { printf(”Tiedostoa ei voi avata”); exit (0); } printf(”Luetaan tietoalkioita tiedostosta \n”); fread(&data2, sizeof(data), 1, fileptr); fclose(fileptr); printf(”Taulukon jäsenet ovat \n”); for (count = 0; count < ITEMS; count++) printf(”Alkio %d on %d\n”, count, data2[count]); return 0; }




Ohjelma tallentaa seitsemän alkion taulukon käyttäjän määrittelemään levytiedostoon. Tämän jälkeen luetaan tiedot takaisin erilliseen taulukkoon ja tulostetaan ne näytölle.