Índex

 
Capítol10.zip Exercicis10.zip
Capítol 10

Entrada i sordida de dades cap a fitxers de disc.

  1. Les funcions d'obertura i tancament d'un fitxer: fopen() i fclose()
  2. Les funcions getc() i putc().
  3. Les funcions fgets() i fputs().
  4. Les funcions fprintf() i fscanf().
  5. Accés aleatori. Les funcions fseek(), ftell(). Les funcions de lectura i escriptura en bloc fread() i fwrite().
    1. La funció fseek()
    2. La funció ftell()
    3. La funció fwrite()
    4. La funció fread().
    5. Desenvolupament del programa gestor del fitxer biblio
  6. Exercicis.

 

10.1. Les funcions d'obertura i tancament d'un fitxer: fopen() i fclose()

La funció fopen() permet obrir un fitxer de disc segons els següents formats.
  1. Si és un fitxer D.O.S. en els formats Text i Binari. El mode de text permet utilitzar totes les funcions d'escriptura i lectura d'un fitxer fent algunes conversions entre el format de les dades de pantalla i de disc. En el format binari s'escriuen les dades en el mateix format que s'envien.
  2. En un fitxer UNIX només n'hi ha un format i és el format equivalent al Binari del D.O.S.
Examinem les següents instruccions que obren un fitxer de disc:
FILE *FitxerFrases
FitxerFrases = fopen("a:prova.txt","w")
En aquestes instruccions veiem:
FILE Nou tipus de dades de punters. Està definit al fitxer de capçalera stdio.h
FitxerFrases La funció fopen retorna aquest punter a un buffer de memòria a on s'emmagatzemen les dades de manera temporal fins que s'omple i es graven en disc si és d'escriptura o van a la memòria si és de lectura. 

Si no hi ha èxit en l'obertura del fitxer, (El disc està ple, defectuós, és una disquetera sense tancar, etc.) la funció fopen retorna el punter a NULL. En realitat és un punter a l'adreça /0 que no existeix. 

"a:prova.txt" És el nom del fitxer en format del sistema operatiu, en aquest cas el DOS, que a més indica la disquetera a on es gravaran les dades. 
"w" Mode d'obertura del fitxer, en aquest cas només d'escriptura i truncant totes les dades. 

Una vegada que hem obert un arxiu podem fer ús de les distintes sentències de lectura i escriptura. Al final de totes les operacions és necessari tancar el fitxer:

fclose(FitxerFrases)
Aquesta instrucció fa que tot el que pugui haver al buffer es descarregui i gravi totes les dades al disc. A més fa que el sistema operatiu actualitzi el seu directori de disc amb les dades corresponents a l'estat final del fitxer. Si no es fa aquesta instrucció és mol possible que les dades es perdin.
La funció return de la funció principal (main{}) també fa que es descarreguin els buffers de fitxers oberts per la funció fopen() i tanca els fitxers.

La funció exit() de la llibreria <stdlib.h> també tanca els fitxers oberts descarregant els buffers. Heu de pensar que aquesta funció només serveix per sortir en cas de qualsevol anomalia en el programa. Al quadre següent estan relacionades les diferents maneres d'obrir un fitxer amb la funció fopen().
 
Mode d'obertura amb fopen()
Modes Significat
"r" Obre un arxiu de text només per lectura.
"w"  Obre un arxiu de text per escriptura, esborra el contingut de l'arxiu anterior del mateix nom. Si l'arxiu no existeix crea un de nou.
"a" Obre un arxiu de text per escriptura, afegint les dades al final de l'arxiu si ja existeix. Si no existeix crea un de nou.
"r+" Obre un arxiu de text per actualització, es a dir, per lectura i escriptura.
"w+" Obre un arxiu de text per actualització (lectura i escriptura), però esborra el contingut de l'arxiu anterior del mateix nom. Si l'arxiu no existeix crea un de nou.
"a+" Obre un arxiu de text per actualització (lectura i escriptura), afegint les noves dades al final del fitxer ja existent del mateix nom. Si l'arxiu no existeix crea un de nou. Es pot llegir l'arxiu complet, però només es pot escriure al final de l'arxiu.
"rb", "wb", "ab",
"r+b", "w+b", "a+b",
"rb+", "wb+", "ab+" 
Fan el mateix que les anteriors però amb arxius binaris en lloc de arxius de text.

Caldria dir que la funció fclose() retorna un valor 0 si ha tingut èxit en tancar el fitxer i un valor distint de 0 si no ha tingut èxit. Així podíem haver posat per tancar la instrucció

if (fclose(FitxerFrases) != 0) printf("Error en tancar el fitxer);

10.2. Les funcions getc() i putc().

Son l'equivalent a getchar() i putchar() però dirigides a fitxer. Suposem que tenim el buffer de FitxerCaracters amb les dades de la figura:

Ja veus que el buffer disposa d'un punter que apunta a la primera lletra. Cada vegada que fem una instrucció ch=getc(FitxerCaracters) la funció getc retorna el caràcter apuntat pel punter, esborra aquest caràcter del buffer i fa que el punter apunti al caràcter següent. A més s'encarrega d'omplir el buffer amb més dades de disc si és necessari.

La funció putchar(Caracter,FitxerFrases) fa que el valor de la variable Caràcter s'afegeixi al buffer d'escriptura corresponent. També es fa càrrec de descarregar el buffer en disc en el moment que aquest està ple.

En el programa de la pàgina següent veiem l'ús de totes aquestes funcions.
 

/*** Demo 1 d'escriptura i lectura en un fitxer de text caracter a caracter. ***/ 

#include <stdio.h>
#include <conio.h> 

void GravaFitxer(void);
void LlegeixFitxer(void); 

void main(void)
{
    int Salir=0; 

    while (! Salir) 
    {
       char Opcio='d';
       clrscr();
       gotoxy(3,5);printf("1.-Escriu una frase al fitxer");
       gotoxy(3,6);printf("2.-Llegir el fitxer");
        gotoxy(3,3);printf("0.-Sortir");
       while((Opcio<'0') || (Opcio>'2')) 
       {
            gotoxy(10,11);printf("Tria una opció ");
            Opcio=getch();
       }
       switch (Opcio)
       {
          case '0' : Salir = 1; break;
          case '1' : GravaFitxer(); break;
          case '2' : LlegeixFitxer();break;
       }
    }

void GravaFitxer(void)
{
    char Caracter=' ', ch=' '; 

    FILE *FCaracter;
    FCaracter = fopen("a:prova.txt","w");
    if (FCaracter != NULL) 
    {
           gotoxy(10,13); printf("Escriu una frase i la gravaré‚ en disc amb el nom <prova.txt>");
           gotoxy(10,14);
           while ((Caracter=getchar()) != '\n') putc(Caracter,FCaracter);
           fclose(FCaracter);
    }
    else 
    {
           gotoxy(10,13); printf("No puc obrir el fitxer");
    }
    gotoxy(10,15); printf("Prem <return per a continuar");
    while((ch=getchar())!='\n');

void LlegeixFitxer(void)
{
    char Caracter,ch; 

    FILE *FCaracter;
    FCaracter = fopen("a:prova.txt","r");
    gotoxy(10,13);
    if (FCaracter != NULL)
    {
           while ((Caracter = getc(FCaracter)) != EOF) putchar(Caracter);
           fclose(FCaracter);
    }
    else  printf("No puc obrir el fitxer");
    gotoxy(10,15); printf("Prem <return> per continuar");
    while((ch=getchar())!='\n');

10.3. Les funcions fgets() i fputs().

Fan lectures i escriptures de strings en el disc.
La funció fputs(): Veiem que les següents instruccions obren el fitxer a:prova.txt per escriptura:
FILE *FFrase;
FFrase = fopen("a:prova.txt","w");
Ara la instrucció següent fa que el string es gravi al fitxer:
fputs("Primer fitxer amb frases\n",FFrase);
Observa que ham afegit un \n al string perquè desprès es distingeixi entre un string i el següent. Sinó els gravaria tots seguits i no els podíem distingir.

La funció fgets(): Obté un string d'un fitxer de disc. Es fa com a la instrucció següent:

Punter=fgets(Frase,250,FFrase)
Frase És el nom d'un punter a un char (char frase[250]).
250 És el número màxim de caràcters que s'han d'incloure al string Frase.
La funció fgets() examina tots els caràcters que afegeix a la variable Frase. Si es troba abans del màxim un caràcter return deixa de llegir caràcters. 
FFrase És el punter al buffer de disc.
Punter Toma el valor NULL si ha arribat al fi del fitxer i no pot seguir llegint.

Cal dir que si troba un return l'incorpora al string.
 

/*Demo 2 d'escriptura i lectura en un fitxer de text en aquest cas frase tres frase.*/ 

#include <stdio.h>
#include <conio.h> 

void GravaFitxer(void);
void LlegeixFitxer(void); 

int main(void)
{
     //Escriure aqui el mateix programa principal que el de la Demo 1

void GravaFitxer(void)

    char ch; 

    FILE *FFrase;
    FFrase = fopen("a:prova.txt","w");
    if (FFrase != NULL)
    {
       fputs("Primer fitxer amb frases\n",FFrase);
       fputs("Aquesta és la segona frase que es grava\n",FFrase);
       fputs("La tercera i última\n",FFrase);
       fclose(FFrase);
    }
    else 
    {
       gotoxy(10,13); printf("No puc obrir el fitxer");
    }
    gotoxy(10,21); printf("Prem <return>  per continuar");
    while((ch=getchar())!='\n');

void LlegeixFitxer(void)
{
    char Frase[250],ch; 

    FILE *FFrase;
    FFrase = fopen("a:prova.txt","r");
    gotoxy(1,13);
    if (FFrase != NULL) 
    {
       while (fgets(Frase,250,FFrase) != NULL) printf("%s",Frase);
       fclose(FFrase);
    }
    else printf("No puc obrir el fitxer");
    gotoxy(10,17); printf("Prem <return> per continuar");
    while((ch=getchar())!='\n');

En aquest programa només es varia la funció GravaFitxer per fer que ens demani pel teclat las frases que volem gravar.
 

/*Demo 3 d'escriptura i lectura en un fitxer de text en aquest cas amb frases.*/
/* Només canvia la funció GravaFitxer respecte a la demo 2*/
void GravaFitxer(void)
{
    char Frase[255], ch=' '; 

    FILE *FFrase;
    FFrase = fopen("a:prova.txt","w");
    if (FFrase != NULL) 
    {
       for(int i=1;i<4;i++)
       {
          gotoxy(1,12+2*i);
          printf("Escriu la frase %d i la gravar‚ en disc amb el nom <prova.txt>\n",i);
          fflush(stdin);
          gets(Frase);
          fputs(Frase,FFrase);fputs("\n",FFrase);
       }
       fclose(FFrase);
    }
    else 
    {
       gotoxy(10,13);
       printf("No puc obrir el fitxer");
    }
    gotoxy(10,21);
    printf("Prem <return> per continuar");
    while((ch=getchar())!='\n');

10.4. Les funcions fprintf() i fscanf().

Aquestes dues funcions fan el mateix que les funcions de printf i scanf, però a més necessiten un argument més, que és el primer argument i és el punter al fitxer en el que es gravaran les dades.

En el programa es fa ús per primera vegada de la funció rewind() que fa que el programa es dirigeixi al principi de l'arxiu.

A més es fa ús per primera vegada del mode "a+" per obrir l'arxiu, es a dir que serà un arxiu obert per lectura i escriptura.

El programa afegeix paraules a la fi de l'arxiu cada cop que s'executa i a més informa del contingut de l'arxiu.
 

#include <stdio.h>
#include <stdlib.h>
void main(void) 
{
    FILE *FPalabras; 

    char palabras[20];
    int aux;
    if ((FPalabras = fopen("a:palabras", "a+")) == NULL)
    {
       fprintf(stdin, "No puedo abrir el fichero \"palabras\".\n");
       exit(1);
    }
    puts("Introduzca texto a añaadir al fichero; pulse la tecla");
    puts("Intro al comienzo de una línea para terminar.");
    while (gets(palabras) != NULL && palabras[0] != '\0')
    fprintf(FPalabras, "%s ", palabras);
    puts("Contenido del fichero:");
    rewind(FPalabras);        /* retrocede al comienzo del FPalabraschero */
    while (fscanf(FPalabras, "%s", palabras) == 1) puts(palabras);
    if ((aux = fclose(FPalabras)) != 0) fprintf(stderr, "Error al cerrar fichero\n");

10.5. Accés aleatori. Les funcions fseek(), ftell(). Les funcions de lectura i escriptura en bloc fread() i fwrite().

En aquest apartat farem un fitxer molt senzill de una biblioteca. Definim l'estructura de dades següent:
 
/* prepara patró d'estructura */
struct biblio 
{
    char Titol[40]; 

    char Autor[40];
    unsigned int Preu;
}; 

Es veu que cada registre ocupa en total 82 bytes. Heu de pensar que una variable de tipus sencer només ocupa 2 bytes.

Suposem que hem fet ja un fitxer amb quatre registre d'aquest tipus, fent la copia exacta dels registres tal i com estan a la memòria. Ens trobarem al disc amb un fitxer com el de la figura.

Notem dues coses que hem fet per representar el contingut del disc:

  1. El símbol  representa el codi ASCII "\0" de acabament d'un string.
  2. El preu està representat en el format de memòria és a dir quan posem (210)(4) volem dir que al disc hi ha escrit el codi ASCII 210 i el codi ASCII 4. La manera d'obtenir el preu és 4*256+210=1234.
Així aquí tenim un fitxer en el que: · Hi ha quatre registres.
· Cada registre ocupa 82 bytes.
· El primer caràcter és "D"
· El caràcter que ocupa lloc 87 és la "v" de verbs
. Hi ha en total 82*4 caràcters.

10.5.1. La funció fseek()

Ja saps que disposem de un punter que es mou apuntant als diferents caràcters. La funció fseek() permet moure aquest punter.

Aquesta funció rep tres paràmetres:

  1. El fitxer que ha d'utilitzar.
  2. El número de bytes que ha de desplaçar el punter.
  3. La posició des-de la que ha de començar a comptar els bytes. Aquestes posicions poden ser:
                        SEEK_SET        Primer caràcter del punter.
                        SEEK_CUR        Posició actual del punter.
                        SEEK_END        Final del fitxer
Així:
fseek(PLlibres,23,SEEK_SET)             posa el punter a la posició 23: h.
fseek(PLlibres,82*3,SEEK_SET)   posa el punter després del tercer registre
fseek(PLlibres,0,SEEK_END)              posa el punter a la posició final del fitxer.
etc...
Aquesta funció retorna un 0 si ha mogut el punter amb èxit, però no es molt fiable.

10.5.2. La funció ftell()

Retorna la posició actual del punter en forma de longint. Pos=ftell(FBiblio). 

10.5.4. La funció fwrite()

Aquesta funció escriu els bytes de la memòria a un fitxer de disc igual que estan en la memòria.

Aquesta funció rep quatre paràmetres:

  1. Direcció de memòria on ha de començar.
  2. Número de bytes que ha de gravar.
  3. Número de blocs que ha de gravar.
  4. Fitxer que ha de rebre les dades.
Suposem que tenim la variable de memòria següent:
struct Biblio Llibre = {"Don quijote de la mancha","Miguel de Cervantes",1234}

La instrucció per gravar aquesta fitxa és:

fwrite(&Llibre, sizeof(struct biblio), 1, pLlibres)

Si la funció ha tingut èxit al gravar retorna el número de bocs escrit al disc. Així si la instrucció anterior retornarà un 1.

10.5.4. La funció fread().

És la funció inversa: llegeix fitxers de disc i els posa a la memòria tal i com estan escrits al disc.

Rep quatre paràmetres:

  1. Direcció de memòria on es posarà les dades llegides dels disc.
  2. Número de bytes que ha de llegir en cada bloc.
  3. Número de blocs.
  4. Fitxer del que ha de llegir.
Així les ordres per llegir del nostre fitxer el registre 3 seran:
        struct Biblio Llibre
        fseek(pLlibres,2*sizeof(struct biblio),SEEK_SET)
        fread(&Llibre, sizeof(struct biblio),1, pLlibres)
A partir d'ara tindrem a la memòria la variable Llibre amb les dades del tercer llibre. Podem fer instruccions com les següents:
        printf("Títol: %s\n",Llibre.Titol);
        printf("Autor: %s\n",Llibre.Autor);
        printf("Preu: %u\n",Llibre.Preu);

10.5.5. Desenvolupament del programa gestor del fitxer biblio

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h> 

/* prepara patró d'estructura */
struct biblio 
{
    char Titol[40]; 

    char Autor[40];
    unsigned int Preu;
}; 

FILE *pLlibres; 

int Mida = sizeof(struct biblio); 

void ObreFitxer(void);
void AfegeixRegistreAlFitxer(void);
void LlegeixRegitreDelFitxer(void);
void LlegeixTotElFitxer(void);
void TancaFicher(void); 

int main(void)
{
    int Fer=1; 

    while(Fer) 
    {
       char Opcio='7';
       clrscr();
       gotoxy(3,3);printf("Programa gestor del fitxer a:Llibre.dat");
       gotoxy(5,5);printf("0 Sortir.");
       gotoxy(5,7);printf("1 Obrir el fitxer");
       gotoxy(5,8);printf("2 Afegir un registre al fitxer.");
       gotoxy(5,9);printf("3 Llegir un registre del disc.");
       gotoxy(5,10);printf("4 Llegir tots els registres del disc.");
       gotoxy(5,11);printf("5 Tancar el fitxer");
       gotoxy(5,13);printf("Tria una opció ");
       while (Opcio<'0' || Opcio > '5') Opcio=getchar();
       switch (Opcio) 
       {
          case '0' : Fer=0; break;
          case '1' : ObreFitxer(); break;
          case '2' : AfegeixRegistreAlFitxer(); break;
          case '3' : LlegeixRegitreDelFitxer(); break;
          case '4' : LlegeixTotElFitxer(); break;
          case '5' : TancaFicher(); break;
       }
    }
    return 0;

void ObreFitxer(void)
{
    char ch = '0'; 

    pLlibres=fopen("a:Llibre.dat","a+b");
    gotoxy(5,15);
    if (pLlibres == NULL) printf("No he pogut obrir el fitxer");
    else printf("Fitxer obert correctament");
    fflush(stdin);while ((ch=getchar()) != '\n');

void AfegeixRegistreAlFitxer(void)
{
    biblio Llibre; 

    char ch='0';
    clrscr();
    puts("Introdueix les dades de un llibre i les gravar‚ al final del fitxer\n");
    fflush(stdin);printf("Introdueix el titol: "); gets(Llibre.Titol);
    fflush(stdin);printf("Introdueix l'autor : "); gets(Llibre.Autor);
    fflush(stdin);printf("Introdueix el preu: "); scanf("%u",&Llibre.Preu);
    fseek(pLlibres,0L,SEEK_END);
    if (fwrite(&Llibre,Mida,1,pLlibres) == 1)
           printf("He escrit b‚ el registre al buffer del fitxer");
    else
           printf("No he pogut escriure el registre al buffer del fitxer");
    fflush(stdin);while ((ch=getchar()) != '\n');

void LlegeixRegitreDelFitxer(void)
{
    int NumRegistre; 

    biblio Llibre;
    char ch='0';
    clrscr();
    printf("Escriu el número del registre que vols llegir: ");
    scanf("%d",&NumRegistre);
    if ((fseek(pLlibres,(NumRegistre-1)*Mida,SEEK_SET) == 0) &&
       (fread(&Llibre, Tamany,1, pLlibres) == 1)) 
    {
       printf("Títol: %s\n",Llibre.Titol);
       printf("Autor: %s\n",Llibre.Autor);
       printf("Preu: %u\n",Llibre.Preu);
    }
    else
       printf("No he pogut anar a aquest registre");
    fflush(stdin);while ((ch=getchar()) != '\n');

void LlegeixTotElFitxer(void)
{
    char ch = '0';
    biblio Llibre;
    int cont=0;
    rewind(pLlibres); // va al principi del fitxer
    clrscr();
    while(fread(&Llibre, Mida,1, pLlibres) == 1)
    {
       printf("%s por %s: %u pts.\n", Llibre.Titol,Llibre.Autor, Llibre.Preu);
       cont++;
    }
    if (! cont )
       printf("No he pogut llegir cap registre");
    else
       printf("He llegit %d llibres",cont);
    fflush(stdin);while ((ch=getchar()) != '\n');

void TancaFicher(void)
{
    char ch='0'; 

    gotoxy(5,15);
    if (fclose(pLlibres) != 0)
       printf("No he pogut tancar el fitxer");
    else
       printf("Fitxer tancat correctament");
    fflush(stdin);while ((ch=getchar()) != '\n');

10.6. Exercicis.

  1. Amb el programa Demo1 escriu dos fitxers nomenats a:text1.txt i a:text2.txt que continguin un mínim de 30 caràcters.
    1. Fes un programa que crearà un nou fitxer a:text3.txt que consti dels caràcters de text1.txt i dels de text2.txt uns darrera els altres.
    2. Afegeix a aquest programa la instruccióió de llegir el fitxer text3.txt i posar-lo en pantalla.
  2. Amb el programa Demo3 fes dos fitxers de frases a:frases1.txt i a:frases2.txt que continguin un mínim de 3 frases cadascun.
    1. Fes un programa que crearà un nou fitxer a:frases3.txt que tingui les frases de dels fitxers frases1.txt i frases2.txt.
    2. Afegeix a aquest programa la instrucció de llegir el fitxer frases3.txt i posar-lo en pantalla.
  3. Afegeix al programa de la biblioteca l'opció de corregir un registre.

  4.  
  5. Amb el programa de la biblioteca fes dos fitxers de biblioteca: a:biblio1.dat i a:biblio2.dat. Aquest dos fitxers han de tenir un mínim de tres llibres cadascun.
    1. Fes un programa que crearà el fitxer a:biblio3.dat que contingui les dades dels dos fitxers.
    2. Afegeix a aquest programa l'opció de llegir el fitxer biblio3.dat i posar-lo en pantalla.
  6. Escriu un fitxer amb el tractament de text Word que consti com a mínim de tres paràgrafs i guarda-lo com a fitxer ASCII (Fitxer de text). Fes un programa en C que llegeixi aquest fitxer. Pensa en el format que has d'utilitzar.

  7.  
  8. Fes un programa que gestioni un fitxer de dades personals. El format del programa pot ser semblant al del programa de la biblioteca.

  9.  
  10. Realitza les següents accions:
    1. Fes un fitxer de base de films amb Acces.
    2. Graba les dades en format ASCII (Texto delimitado).
    3. Mitjançant un tractament de textos qualsevol observa el format del fitxer que has creat amb l'Acces.
    4. Fes el programa en C que sigui capaç de llegir i presentar en pantalla aquest fitxer.
  11. Realitza les següents accions:
    1. Fes ús del fitxer de base de dades del problema anterior i graba les dades en format ASCII (Texto amb amplitud fixa).
    2. Mitjançant un tractament de textos qualsevol observa el format del fitxer que has creat amb l'Acces.
    3. Fes el programa en C que sigui capaç de llegir i presentar en pantalla aquest fitxer.