Capítol 10
Entrada i sordida de dades cap a fitxers de disc.
-
Les funcions d'obertura i tancament d'un fitxer: fopen()
i fclose()
-
Les funcions getc() i putc().
-
Les funcions fgets() i fputs().
-
Les funcions fprintf() i fscanf().
-
Accés aleatori. Les funcions fseek(), ftell().
Les funcions de lectura i escriptura en bloc fread() i fwrite().
-
La funció fseek()
-
La funció ftell()
-
La funció fwrite()
-
La funció fread().
-
Desenvolupament del programa gestor del fitxer biblio
-
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.
-
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.
-
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.
![](imagenes/cap1002.gif)
Notem dues coses que hem fet per representar el contingut del disc:
-
El símbol
representa el codi ASCII "\0" de acabament d'un string.
-
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:
-
El fitxer que ha d'utilitzar.
-
El número de bytes que ha de desplaçar el punter.
-
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:
-
Direcció de memòria on ha de començar.
-
Número de bytes que ha de gravar.
-
Número de blocs que ha de gravar.
-
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:
-
Direcció de memòria on es posarà les dades llegides
dels disc.
-
Número de bytes que ha de llegir en cada bloc.
-
Número de blocs.
-
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.
-
Amb el programa Demo1 escriu dos fitxers nomenats a:text1.txt i a:text2.txt
que continguin un mínim de 30 caràcters.
-
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.
-
Afegeix a aquest programa la instruccióió de llegir el fitxer
text3.txt i posar-lo en pantalla.
-
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.
-
Fes un programa que crearà un nou fitxer a:frases3.txt que tingui
les frases de dels fitxers frases1.txt i frases2.txt.
-
Afegeix a aquest programa la instrucció de llegir el fitxer frases3.txt
i posar-lo en pantalla.
-
Afegeix al programa de la biblioteca l'opció de corregir un registre.
-
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.
-
Fes un programa que crearà el fitxer a:biblio3.dat que contingui
les dades dels dos fitxers.
-
Afegeix a aquest programa l'opció de llegir el fitxer biblio3.dat
i posar-lo en pantalla.
-
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.
-
Fes un programa que gestioni un fitxer de dades personals. El format del
programa pot ser semblant al del programa de la biblioteca.
-
Realitza les següents accions:
-
Fes un fitxer de base de films amb Acces.
-
Graba les dades en format ASCII (Texto delimitado).
-
Mitjançant un tractament de textos qualsevol observa el format del
fitxer que has creat amb l'Acces.
-
Fes el programa en C que sigui capaç de llegir i presentar en pantalla
aquest fitxer.
-
Realitza les següents accions:
-
Fes ús del fitxer de base de dades del problema anterior i graba
les dades en format ASCII (Texto amb amplitud fixa).
-
Mitjançant un tractament de textos qualsevol observa el format del
fitxer que has creat amb l'Acces.
-
Fes el programa en C que sigui capaç de llegir i presentar en pantalla
aquest fitxer.