Helsingin yliopisto Tietojenkäsittelytieteen laitos
 

Tietojenkäsittelytieteen laitos

Tietoa laitoksesta:

 
Helsingin yliopisto / Tietojenkäsittelytieteen laitos / Copyright © 2000 Jan Lindström. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

58127-1 C-ohjelmointi : 10 Tiedostot


  • 10 Tiedostot

    10 Tiedostot

    Tiedostojen käyttö on aika helppoa C-kielessä. Ennen tiedoston käyttöä tiedosto täytyy avata. Tämä tapahtuu funktiolla FILE *fopen(char *TiedostoNimi, char *Tapa, missä TiedostoNimi on tiedoston nimi merkkijonossa ja Tapa kertoo tiedoston käsittelytavan. Tapa kentässä voi olla seuraavia merkkejä:

    • "r": tiedostoa luetaan
    • "w": tiedostoon kirjoitetaan
    • "a": tiedostoon loppuun kirjoitetaan lisää dataa (append)
    • "b": tiedostoa käsitellään binääritietoa ei tekstitietona (ei voi käyttää yksinään vaan vaatii yhden ylläolevista)
    Mahdollisia Tapa kentän arvoja on siis myös:
    • "rb" : lue binääritiedostoa
    • "wb" : kirjoita binääritietoa
    • "ab" : lisää tiedoston loppuun binääritietoa
    Funktio palauttaa osoittimen tyyppiin FILE tai NULL, jos tiedostoa ei voida avata. Esimerkkejä:

         FILE *file;
         file = fopen ( "tiedosto.txt", "r" );
    
         FILE *Kuva = fopen ( "kuva.jpg", "wb" );
    
    Avattu tiedosto täytyy aina muistaa sulkea funktiolla fclose(). Esimerkiksi:

         fclose ( Kuva );
    

    10.1 Tiedostoon kirjoittaminen

    Tiedostoon kirjoittamiseen löytyy muutama erilainen funktio. Merkeittäin voi kirjoittaa funktiolla fputc(int Merkki,FILE *Tiedosto). Funktio palauttaa kirjoitetun merkin jos kirjoitus onnistui tai EOF jos sattui virhe.

    Kokonaisen merkkijonon voi kirjoittaa tiedostoon funktiolla fputs(char *MerkkiJono,FILE *Tiedosto). Palauttaa positiivisen kokonaisluvun jos kirjoitus onnistui tai EOF jos sattui virhe. Esimerkiksi:

    #include <stdio.h>
    
    int main(void) 
    {
        int Luettu;
        FILE *Tiedosto;
           
        /* Avataan tiedosto kirjoittamista varten */
        if ( (Tiedosto = fopen ( "tiedosto.txt", "w" )) == NULL ) 
        {
             /* Avaaminen epäonnistui */
             printf ( "Tiedostoa \"tiedosto.txt\" ei voitu avata 
               kirjoittamista varten.\n" );
             exit ( 1 );
        }
    
        /* Kirjoita ensin viesti */
        fputs ( "Alla on näppäimistöltä syötetty teksti:\n", Tiedosto );
           
        /* lue merkki kerralla kunnes tulee virhe tai EOF */
        while ( ( Luettu = getchar () ) != EOF ) 
        {
             /* tulosta luettu merkki */
             fputc ( Luettu, Tiedosto );
        }
    
        /* suljetaan tiedosto */
        fclose ( Tiedosto );
           
        return 0;
    }
    

    Eri funktioita voi käyttää samaan tiedostoon kirjoittamiseen. Funktiot löytyvät otsikkotiedostosta <stdio.h>.


    10.2 Formatoitu kirjoittaminen

    Formatoidun kirjoittamisen funktiota fprintf() voidaan käyttää jos haluaa formatoida kirjoitetun tekstin. Toimii kuten normaali printf(), jolla on lisäksi FILE * ensimmäisenä parametrina (eli tiedosto-osoitin, joka saadaan tiedostoa avattaessa). Esimerkki:
         /* Haetaan dataa jostain */
         int Ika = 22;
         char *Nimi = "Markku Kataja";
         char *Juuri = "henkilo";
    
         /* avataan tiedosto */
         FILE *Data = fopen ( "uusi.xml", "w" ); 
    
         fprintf ( Data, "<?xml version=\"1.0\"?>" );
         fprintf ( Data, "<%s>\n", Juuri );
         fprintf ( Data, "<nimi>%s</nimi><ika>%d</ika>\n", Nimi, Ika );
         fprintf ( Data, "</%s>\n", Juuri );
    
         /* valmista, suljetaan tiedosto */
         fclose ( Data );
    
         /*
         ohjelman ajo antaa tuloksena tiedoston uusi.xml joka sisältää: 
         <?xml version="1.0"?>
         <henkilo>
         <nimi>Markku Kataja</nimi><ika>22</ika>
         </henkilo>
         */
    

    10.3 Binääritiedon kirjoittaminen

    Binääritietoa kirjoitetaan funktiolla fwrite(void *Data, size_t Koko, size_t Maara, FILE *Tiedosto).

    • Data voi olla osoitin mihin tahansa tyyppin.
    • Koko on yhden elementin koko.
    • Maara on elementtien määrä, esim. kirjoittaessa taulukkoa.
    • Tiedosto on moodissa "wb" avattu tiedosto
    Funktio palauttaa kirjoitettuen elementtien lukumäärän, eli parametrin Maara. Jos tapahtuu virhe palautusarvo on pienempi kuin Maara. Esimerkki:

    #include <stdio.h>
    
    int main(void)
    {
        int Taulukko[10] = { 1,2,3,4,5,6,7,8,9,10 };
        FILE *OutFile;
           
        / Avaa tiedosto binääridatan kirjoitusta varten */
        if ( (OutFile = fopen ( "binaari.bin", "wb" )) == NULL ) 
        {
             /* Avaaminen epäonnistui */
             printf ( "Tiedostoa \"binaari.bin\" ei voitu avata" );
             printf ( "kirjoittamista varten.\n");
             exit ( 1 );
        }
    
        /* Kirjoita koko taulukko tiedostoon *(
        if ( fwrite ( Taulukko, sizeof (int), 10, OutFile ) != 10 ) 
        {
             /* Kirjoitus epäonnistui */
             printf ( "Virhe kirjoitettaessa tiedostoon!");
        }
    
        /* sulje tiedosto */
        fclose ( OutFile );
           
        return 0;
    }
    

    Myös tietueita sisältävän taulukon voi kirjoittaa. Esimerkiksi:

    struct Aika 
    {
        unsigned short Tunti;
        unsigned short Minuutti;
        unsigned short Sekunti;
        unsigned short Millisekunti;
    }
    
    /* pieni kalenteri */
    struct Aika Kalenteri[200];
    
    int main(void)
    {
    /* kaikenlaista tapahtumaa ... */
    
    /* Kijoitetaan kalenteri avoimeen tiedostoon */
         if ( fwrite ( Kalenteri, sizeof(struct Aika), 200, Tiedosto ) != 200 )
         {
             /* sattui virhe
             .... */
         }
    /* Tehdään kaikkea muuta...*/
    }
    

    10.4 Tiedostosta lukeminen

    Tiedostosta voi lukea merkki kerrallaan funktiolla fgetc(FILE *Tiedosto). Funktio palauttaa kirjoitetun merkin konvertoituna tyypiksi int tai vakion EOF jos syntyi virhe.

    Kokonaisen merkkijonorivin voi lukea funktiolla fgets(char *Merkkijono, int Koko, FILE *Tiedosto). Funktio lukee maksimissaan Koko -1 merkkiä ja tallettaa ne muuttujaan Merkkijono. Funktio lopettaa lukemisen jos luetaan rivinvaihtomerkki ennen kuin Koko merkkiä on luettu. Viimeisen luetun merkin perään funktio asettaa merkin '\0'. Palauttaa muuttujan Merkkijono jos luku onnistui tai NULL-osoitteen jos syntyi virhe. Esimerkki:

    #include <stdio.h>
    
    int main (int argc, char **argv) 
    {
       int Luettu;
       FILE *Input;
       FILE *Output;
    
       /* Varmista ohjelman parametrien lukumäärä */
       if ( argc != 3 ) {
         printf ( "Väärät parametrit!\nKäyttö: %s alkuperäinen", argv[0] );
         printf ( " kopio\n" );
         exit ( 1 );
       }
         
       /* Avaa alkuperäinen tiedosto lukua varten */
       if ( (Input = fopen ( argv[1], "r" )) == NULL ) 
       {
         /* Avaaminen epäonnistui */
         printf ( "Tiedostoa %s ei voitu avata.", argv[1]);
         exit ( 1 );
       }
         
       /* Avataan tiedosto kirjoittamista varten */
       if ( (Output = fopen ( argv[2], "w" )) == NULL )
       {
         /* Avaaminen epäonnistui */
         printf ( "Tiedostoa %s ei voitu avata.", argv[2]);
         exit ( 1 );
       }
       
       /* Lue merkki kerralla kunnes tulee virhe tai EOF */
       while ( ( Luettu = fgetc ( Input ) ) != EOF ) {
         /* kirjoita merkki kopioon */
         fputc ( Luettu, Output );
       }
    
       /* suljetaan tiedostot */
       fclose ( Input );
       fclose ( Output );
       
       return 0;
    }
    

    10.5 Formatoitu lukeminen tiedostosta

    Funktiolla fscanf() voidaan lukea formatoitua tietoa tiedostosta samaan tapaan kuin scanf() funktiolla, mutta lisäksi ensimmäisenä parametrina on osoitin tiedostoon (FILE *). Funktio palauttaa luettujen parametrien lukumäärän. Esimerkki:

    #include<stdio.h>
    
    int main(void)
    {
        int Luku;
        char Merkki;
        char MerkkiJono[80];
        float Reaaliluku;
        FILE *file;
    
        if (!( file = fopen("r","tiedosto.txt") ))
        {
            printf("Tiedostoa tiedosto.txt ei voi avata.\n");
            exit(1);
        }
    
        if ( ( fscanf(file,"%d%c%s&f",&Luku,&Merkki,MerkkiJono,
          &Realiluku)) != 4 )
        {
            printf("Luku epäonnistui\n");
            exit(1);
        }
    
        /* Tehdään jotain järkevää...*/
    
        fclose(file);
        return 0;
    }
    

    10.6 Binääritiedon lukeminen

    Binääritiedostoa voidaan lukea funktiolla fread(void *Data, size_t Koko, size_t Maara, FILE *Tiedosto).

    • Data on osoitin haluttuun paikkaan ja tyyppiin.
    • Koko on yhden elementin koko
    • Maara on elementtien määrä, esim. luettaessa taulukkoa
    • Tiedosto on moodissa "rb" avattu tiedosto

    Funktio on siis funktion fwrite() vastakohta. Palauttaa luettujen elementtien lukumäärän, eli parametrin Maara. Jos tapahtuu virhe palautusarvo on pienempi kuin Maara. Esimerkki:

    #include <stdio.h>
    
    int main(void) 
    {
        int Taulukko[100];
        int Index, Luettu;
        FILE *Input;
           
        /* Avaa tiedosto binääridatan lukua varten */
        if ( (Input = fopen( "testitiedosto.bin", "rb" )) == NULL ) 
        {
             /* avaaminen epäonnistui */
             printf ( "Tiedostoa testitiedosto.bin ei voitu avata.");
             exit ( 1 );
        }
    
        /* Lue koko taulukko tiedostosta */
        if ( (Luettu = fread( Taulukko, sizeof (int), 10, Input )) != 100 )
        {
             /* Lukeminen epäonnistui */
             printf ( "Lukuvirhe, saatiin %d elementtiä\n", Luettu );
        }
        else 
        {
             /* tulosta saatu taulukko */
             for ( Index = 0; Index < 10; Index++ ) 
               printf ( "%d: %d\n", Index, Taulukko[Index] );
             
        }
    
        /* sulje tiedosto */
        fclose ( Input );
           
        return 0;
    }
    

    10.7 Tiedostot ja virheet

    Tiedoston loppumista voi testata funktiolla feof(FILE *Tiedosto). Funktio palauttaa 0 jos tiedosto on loppu, muuten jonkun toisen arvon.

    Funktiolla ferror(FILE *Tiedosto) voi testata onko tiedosto-operaatiosta rekisteröitynyt virhettä. Funktio palauttaa 0 jos avatulle tiedostolla Tiedosto ei ole rekisteröity virhettä.

    Virhelippu voidaan resetoida funktiolla clearerr(FILE *Tiedosto). Esimerkiksi:

         /* avaa tiedosto lukua varten */
         if ( (Input = fopen ( argv[1], "r" )) == NULL ) 
         {
           /* avaaminen epäonnistui */
           printf ( "Tiedostoa %s ei voitu avata.\n", argv[1] );
           exit ( 1 );
         }
    
         do 
         {
           /* lue rivi tiedostosta */
           fgets ( Puskuri, 1000, Input );
    
           if ( ferror(Input) )
           {
               perror("Tiedoston luvussa sattui virhe: ");
               exit(1);
           }
         }
         while ( ! feof ( Input ) && fputs ( Puskuri, stdout ) );
    



    Jan Lindström (Jan.Lindstrom@cs.Helsinki.FI)