Programació estructurada i modular Unitat didàctica 5 - Dextrus
Programació estructurada i modular Unitat didàctica 5 - Dextrus
Programació estructurada i modular Unitat didàctica 5 - Dextrus
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
CICLES FORMATIUS<br />
Família Professional Informàtica<br />
Cicle formatiu de grau superior<br />
DESENVOLUPAMENT D’APLICACIONS INFORMÀTIQUES<br />
Crèdit 5<br />
<strong>Programació</strong> <strong>estructurada</strong> i <strong>modular</strong><br />
<strong>Unitat</strong> <strong>didàctica</strong> 5: Gestió de fitxers<br />
Albert Canela Campos
Índex<br />
5.1 Fitxers a C ......................................................................................................................................3<br />
5.1.1 Fitxers de text i fitxers binaris ................................................................................................3<br />
5.1.2 Declaració d'un arxiu ..............................................................................................................4<br />
5.1.3 Obertura d'un arxiu .................................................................................................................4<br />
5.1.4 Tancament d’un arxiu .............................................................................................................6<br />
5.2 Fitxers de text.................................................................................................................................7<br />
5.2.1 Escriptura i lectura de caràcters. Les funcions fgetc() i fputc() ..............................................7<br />
5.2.2 Lectura i escriptura amb les funcions fgets() i fputs().............................................................9<br />
5.2.3 Lectura i escriptura de dades formatades. Les funcions fprintf() i fscanf() ..........................12<br />
5.2.4 L’accés directe ......................................................................................................................15<br />
Exercicis.....................................................................................................................................17<br />
Pràctica...............................................................................................................................................19<br />
5.3 Fitxers binaris...............................................................................................................................20<br />
Exercici ......................................................................................................................................24<br />
Pràctica...............................................................................................................................................25<br />
2
5.1 Fitxers a C<br />
El sistema d'entrada i sortida de C proporciona un sistema independent del dispositiu físic en el<br />
qual, o des del qual, es realitzin les operacions. Aquest sistema independent és una abstracció de<br />
dispositiu que rep el nom de flux o corrent (en anglès stream). De fet, només necessitem considerar<br />
un arxiu com un flux de dades que circulen en un o altre sentit.<br />
Tècnicament, quan s'obre un arxiu per utilitzar funcions d'entrada i sortida, aquest arxiu s'associa<br />
amb una estructura del tipus FILE (definit a stdio.h). Aquesta estructura conté tota la informació<br />
bàsica sobre l'arxiu.<br />
5.1.1 Fitxers de text i fitxers binaris<br />
Quan emmagatzemem dades en un fitxer, aquestes es poden emmagatzemar en format text o en<br />
format binari. El format text significa que s’emmagatzema tot com a text, inclosos els números. En<br />
canvi, en format binari, el que es guarda és la representació interna de la computadora per aquell<br />
valor. Per exemple quan volem guardar un enter en un fitxer de text, es guardarà realment els<br />
caràcters que el composen. En canvi, si aquest mateix enter es vol emmagatzemar en un fitxer<br />
binari, enlloc d’emmagatzemar caràcters, s’emmagatzemarà la representació interna de l’ordinador,<br />
d’aquest enter.<br />
La representació binària acostuma a ocupar menys espai que el format de caràcters ASCII dels<br />
mateixos valors. A més, degut a que les dades binàries normalment utilitzen menys espai en disc<br />
que les de text, l’E/S per arxiu binari acostuma a ser més ràpida que l’E/S de text.<br />
No obstant cada format té els seus avantatges. Així doncs, el format text és més fàcil de llegir, i el<br />
podem editar en un editor de text, en un processador de textos, etc. A més, un arxiu binari es pot<br />
transmetre molt fàcilment d’un ordinador a un altre. Per altra banda, el format binari és més precís<br />
per a números, pel que fa a la no existència d’errors de precisió o d’arrodoniment.<br />
Els arxius binaris s’han de tractar de manera diferent als de text, doncs no contenen caràcters<br />
d’espais en blanc, les dades no estan organitzades en línies amb el caràcter d’acabament de nova<br />
línia, etc.<br />
3
5.1.2 Declaració d'un arxiu<br />
Com tota variable, una variable arxiu ha de ser declarada abans d'utilitzar-la. Així, la sentència<br />
FILE *fitxer;<br />
declara una variable d’arxius anomenada fitxer. Aquesta variable, de fet, és un punter a l'objecte<br />
FILE, que és una estructura definida a l'arxiu capçalera stdio.h. Per tant, és necessari incloure la<br />
directiva #include en qualsevol programa en què s’utilitzi arxius.<br />
5.1.3 Obertura d'un arxiu<br />
Declarar una variable d'arxiu no és suficient per poder treballar amb ell. És necessari precisar dues<br />
coses:<br />
D'una banda, és imprescindible assenyalar de quin arxiu físic es tracta. És necessari donar un nom a<br />
l'arxiu que serà el que restarà al suport magnètic.<br />
D'altra banda, farà falta precisar el tipus de treball que volem fer: llegir, escriure,...<br />
Aquestes característiques es donen amb l'ajuda de la funció fopen() que té la següent sintaxi:<br />
FILE *fopen(char *nom_arxiu, char *mode)<br />
La funció fopen() retornarà el valor NULL si no s'ha pogut obrir l'arxiu, per exemple, si el disc està<br />
protegit o ple.<br />
El primer argument de la funció fopen() és el nom físic de l'arxiu i permet l'associació entre el nom<br />
físic sobre el suport (nom_arxiu) i el nom lògic de l'arxiu en el programa.<br />
El segon argument està format per una cadena de caràcters que precisa el mode de treball amb<br />
l'arxiu.<br />
4
Són possibles les diferents possibilitats:<br />
r<br />
w<br />
a<br />
r+<br />
w+<br />
(read). Obre un arxiu només per llegir. L'arxiu ha d'existir. El cursor se situarà damunt del<br />
primer octet de l'arxiu.<br />
(write). Obre un arxiu només per escriure. El cursor se situarà damunt del primer octet de<br />
l'arxiu. Si l'arxiu ja existeix es sobreescriu.<br />
(append). Obre un arxiu per escriure. El cursor se situarà damunt de l'últim octet de l'arxiu<br />
si aquest ja existeix, per tant, serveix per afegir dades a un arxiu. En el cas que l'arxiu no<br />
existeixi funciona igual que w.<br />
Obre un arxiu per llegir i escriure alhora. L'arxiu ha d'existir prèviament. El cursor se situa<br />
al començament de l'arxiu.<br />
Obre un arxiu per llegir i escriure alhora. Si l'arxiu existeix prèviament, s'esborra el seu<br />
contingut.<br />
a+ Obre un arxiu per afegir dades i per llegir. Si l'arxiu no existeix el crearà.<br />
Quan obrim un fitxer amb “a” o bé “a+”, totes les operacions d’escriptura es duen a terme a la fi del<br />
fitxer. Nosaltres podem reposicionar el punter del fitxer utilitzant tècniques de posicionament<br />
directe que veurem més endavant, però cada vegada que fem un write, el punter es mou cap al final<br />
del fitxer automàticament. D’aquesta manera s’evita que es sobreescriguin dades.<br />
Quan especifiquem “r+”, “w+” o bé “a+”, es permet tant lectura com escriptura (es per això que a<br />
aquests modes se’ls hi diu modes d’actualització). No obstant, quan nosaltres passem de fer una<br />
escriptura a fer una lectura, o a l’inrevés, cal que es duguin a terme algunes operacions d’accés<br />
directe (fseek, ftell, etc.) o de neteja de buffer (fflush), que ja veurem més endavant.<br />
Afegint “b” a aquests modes, tractarem fitxers binaris. I serà doncs aquests modes els que<br />
utilitzarem més a les nostres pràctiques (“rb”, “wb”, “ab”, “r+b”, etc. ).<br />
5
5.1.4 Tancament d’un arxiu<br />
Al final del treball sobre l'arxiu, o si més no abans de sortir del programa, és necessari tancar l'arxiu.<br />
Aquest tancament té diferents objectius. El primer consisteix en alliberar el canal atribuït a l'arxiu i<br />
permetre obrir un altre arxiu sense superar el màxim nombre possible d'arxius oberts al mateix<br />
temps. El segon, i gairebé sempre el més important, és buidar el buffer (zona de memòria) associat a<br />
l'arxiu. L'escriptura d'un arxiu no es fa directament al suport físic sinó que les diferents escriptures<br />
són enviades a un buffer per tal d'estalviar accessos al disc que són massa lents. En cas d'acabament<br />
normal del programa, el sistema operatiu s'encarrega de tancar els arxius, però això no sempre és<br />
possible, per exemple quan marxa el llum, si es bloqueja l'ordinador, etc. Per tant és recomanable<br />
cada cert interval de temps tancar i tornar-lo a obrir per evitar pèrdua de dades.<br />
Per tancar ho farem amb la funció fclose() que té la següent sintaxi:<br />
int fclose (FILE *nom_arxiu);<br />
Aquesta funció torna el valor enter 0 si l'arxiu s'ha pogut tancar correctament.<br />
6
5.2 Fitxers de text<br />
5.2.1 Escriptura i lectura de caràcters. Les funcions fgetc() i fputc()<br />
Creeu un nou arxiu del tipus C anomenat m5p01.c i escriviu el següent codi:<br />
//m5p01.c - Creació, escriptura i lectura d'un arxiu -<br />
#include <br />
#include <br />
int main(){<br />
FILE *f;<br />
char car;<br />
//procés d'escriptura<br />
system("cls");<br />
if((f=fopen("arxiu.txt","w"))==NULL)<br />
printf("Error al intentar obrir l'arxiu\n");<br />
else{<br />
printf("Introdueix un text. Prem return per finalitzar. \n");<br />
}<br />
}<br />
while ((car=getchar())!='\n') fputc(car,f);<br />
fclose(f);<br />
//procés de lectura<br />
printf("Lectura de l'arxiu\n");<br />
if((f=fopen("arxiu.txt","r"))==NULL)<br />
printf("Error a l'intentar obrir l'arxiu\n");<br />
else{<br />
while((car=fgetc(f))!=EOF) putchar(car);<br />
putchar('\n');<br />
fclose(f);<br />
}<br />
system("pause");<br />
7
Explicació del programa<br />
La següent sentència és una forma habitual d'obrir un arxiu:<br />
if((f=fopen("arxiu.txt","w"))==NULL)<br />
printf("Error al intentar obrir l'arxiu\n");<br />
En aquest cas, s'obre un arxiu de nom arxiu.txt en mode escriptura (w). La variable f és una variable<br />
punter del tipus FILE. L'estructura FILE està definida a l'arxiu capçalera stdio.h, per tant és<br />
necessari incloure la directiva #include en qualsevol programa que utilitzi arxius. Si la<br />
funció fopen() torna el valor NULL vol dir que ha trobat un error i sortirà el missatge d'error i<br />
sortirà del programa.<br />
La següent línia és la que s'encarrega d'escriure en el fitxer els caràcters entrats per teclat. Això és fa<br />
sempre que el caràcter llegit del teclat amb la funció getchar() no sigui el caràcter de retorn de línia<br />
(\n), és a dir, INTRO. La funció que s'encarrega d'enviar un caràcter al fitxer és la funció fputc(), el<br />
primer argument d'aquesta funció és un caràcter o variable de caràcter i el segon argument és una<br />
variable punter FILE.<br />
Després d'escriure el text, es tanca l'arxiu amb la funció fclose(). És important tancar els arxius<br />
fonamentalment per dues raons:<br />
a) per a no sobrepassar el màxim nombre d'arxius oberts que suporta el sistema operatiu.<br />
b) (gairebé sempre el més important) perquè s'escrigui al disc el que hi hagi al buffer, d'aquesta<br />
forma no es perdrà la informació en cas que es bloquegi l'ordinador.<br />
Per procedir a la lectura de l'arxiu, es torna a obrir aquest, en aquest cas en mode de lectura (r):<br />
La línia:<br />
if((f=fopen("arxiu.txt","r"))==NULL)<br />
printf("Error al intentar obrir l'arxiu\n");<br />
while((car=getc(f))!=EOF) putchar(car);<br />
és la que llegeix un caràcter del fitxer (amb la funció getc()) fins que trobi la marca EOF de final<br />
d'arxiu i escriu el caràcter llegit a la pantalla amb la funció putchar().<br />
8
Les quatre funcions d'entrada i sortida tractades en aquesta pràctica són:<br />
fgetc() Llegeix un caràcter d'un arxiu.<br />
getchar() Llegeix un caràcter de l'entrada estàndard (teclat).<br />
fputc() Escriu un caràcter en un arxiu.<br />
putchar() Escriu un caràcter a la sortida estàndard (pantalla).<br />
5.2.2 Lectura i escriptura amb les funcions fgets() i fputs()<br />
Les funcions fgets() i fputs() permeten llegir i escriure cadenes de caràcters en els arxius<br />
prèviament oberts amb la funció fopen().<br />
La sintaxi de la funció fgets() és:<br />
char *fgets( char *cadena, int rang, FILE *punter_arxiu)<br />
La funció fgets() retorna la cadena llegida en cas que la lectura hagi estat correcta, o bé NULL si no<br />
ha estat possible. El primer argument és un punter al lloc de destí de la cadena que volem llegir. La<br />
lectura s’aturarà quan s’hagi llegit un total de rang-1 caràcters o quan trobem el caràcter de nova<br />
línia. Llavors, quan es dóna una d'aquestes dues condicions, fgets() afegeix el caràcter '\0' (nul) de fi<br />
de cadena. Si la cadena que estem llegint finalitza amb el caràcter de nova línia '\n', llavors la<br />
cadena contindrà el caràcter de nova línia i després el finalitzador de cadena '\0' (nul).Si es produeix<br />
un error, la funció fgets() retornarà la constant de caràcter NULL.<br />
La funció fputs() té per sintaxi:<br />
int fputs( char *str, FILE *punter_arxiu )<br />
i tramet una cadena de caràcters a l'arxiu apuntat pel punter punter_arxiu que és un punter de tipus<br />
FILE. Si es produeix un error, retorna la constant EOF de final d'arxiu. No afegeix el caràcter de<br />
nova línia en la sortida. Retorna l'últim caràcter escrit o el caràcter de final d’arxiu EOF.<br />
9
Per practicar aquestes dues funcions, escriviu el següent codi:<br />
Creeu un nou arxiu del tipus C anomenat m5p02.c i escriviu el següent codi:<br />
// m5p02.c - Lectura i escriptura amb fgets() i fputs() -<br />
#include <br />
#include <br />
int main(){<br />
FILE *punter;<br />
char cadena[80];<br />
if (( punter = fopen ("fitxers.txt","w"))==NULL)<br />
puts("\nNo puc crear l'arxiu fitxers.txt");<br />
else{<br />
printf("Introduiu text de varies linies.\n");<br />
printf("Una linia en blanc per acabar.\n");<br />
while (strlen(fgets(cadena,80,stdin))!=1)<br />
fputs(cadena,punter);<br />
fclose(punter);<br />
}<br />
// lectura<br />
if (( punter = fopen ("fitxers.txt","r"))==NULL)<br />
puts("\nNo puc crear l'arxiu: fitxers.txt");<br />
else{<br />
while (fgets(cadena,80,punter) != NULL)<br />
printf("%s",cadena);<br />
fclose(punter);<br />
}<br />
system("pause");<br />
}<br />
10
Explicació del programa<br />
La lectura es fa amb la sentència:<br />
while (strlen(fgets(cadena,80,stdin)) !=1){<br />
fputs(cadena,punter);<br />
}<br />
Aquesta és una sentència típica de C on es barregen funcions i expressions. La funció fgets() llegeix<br />
una cadena de l'entrada estàndard (teclat) i torna el seu argument si no hi ha hagut cap error, és a<br />
dir: strlen(fgets(cadena,80,stdin)) és equivalent a: strlen(cadena). La funció fgets() manté el<br />
caràcter de nova línia (\n), per tant, si s'introdueix una cadena buida,<br />
strlen(fgets(cadena,80,stdin))) és igual a 1, i per tant surt del bucle while. Mentre la cadena no<br />
sigui buida, escriu el contingut de la cadena de caràcters cadena en l'arxiu apuntat pel punter.<br />
La part de lectura es fa de forma similar,<br />
while (fgets(cadena,80,punter) != NULL)<br />
printf("%s",cadena);<br />
fclose(punter);<br />
La funció fgets() llegeix de l'arxiu apuntat per punter una línia o fins a 80 caràcters i emmagatzema<br />
aquests caràcters en la variable cadena. Aquesta lectura es fa mentre el valor retornat per la funció<br />
no sigui NULL. Aquesta circumstància es produirà si hi ha algun error o es troba al final de l'arxiu.<br />
11
5.2.3 Lectura i escriptura de dades formatades. Les funcions fprintf() i<br />
fscanf()<br />
Les funcions fprintf() i fscanf() es comporten igual que les funcions printf() i scanf() excepte pel<br />
fet que necessiten un argument addicional per apuntar a l'arxiu corresponent. El format general és:<br />
int fprintf(punter_arxiu,"cadena de control",llista d'arguments);<br />
int fscanf (punter_arxiu,"cadena de control",llista d'arguments);<br />
La funció fprintf() retorna el nombre de caràcters enviats a l'arxiu. Si es produeix un error retorna<br />
un número negatiu.<br />
La funció fscanf() retorna el nombre d'elements que ha pogut emparellar. Quan arriba al final de<br />
l'arxiu retorna la constant EOF.<br />
Aquestes funcions són especialment importants quan es vol llegir o escriure dades de diferents<br />
tipus. El següent programa és un bon exemple d'això:<br />
12
Creeu un nou arxiu del tipus C anomenat m5p03.c i escriviu el següent codi:<br />
// m5p03.c - Lectura i escriptura amb fprintf() i fscanf() -<br />
#include <br />
int main(){<br />
FILE *f;<br />
int codi;<br />
char nom[20];<br />
char cognom[20];<br />
float nota;<br />
f = fopen ("prova.txt","w");<br />
printf("\n Introdueix les dades.");<br />
printf("\n posa 0 al codi per finalitzar\n");<br />
if (f!=NULL){<br />
do{<br />
printf("\nIntroduiu el codi...");<br />
scanf(" %d",&codi);<br />
if(codi!=0){<br />
printf("Introduiu el nom... ");<br />
scanf(" %s",nom);<br />
printf("Introduiu el cognom...");<br />
scanf(" %s", cognom);<br />
printf("Introduiu la nota...");<br />
scanf(" %f",¬a);<br />
fprintf(f, "%d %s %s %f\n",codi, nom, cognom, nota);<br />
}<br />
}while (codi!=0);<br />
fclose(f);<br />
}<br />
/* lectura */<br />
printf("\n\n************** lectura ************* \n\n");<br />
if ( (f = fopen ("prova.txt","r"))!=NULL){<br />
while (fscanf(f, "%d %s %s %f",&codi, nom,cognom, ¬a) != EOF)<br />
printf("%d %s %s %.2f\n",codi, nom, cognom, nota);<br />
fclose(f);<br />
}<br />
system("pause");<br />
}<br />
13
Explicació del programa<br />
Es pot comprovar que el funcionament de fprintf() és similar a printf(). Amb una funció scanf() es<br />
llegeix el valor de les variables codi, nom, cognom i nota i després s'escriu el contingut d'aquestes<br />
quatre variables a l'arxiu apuntat per f de la mateixa forma que es faria a la pantalla:<br />
fprintf(f, "%d %s %s %f\n",codi, nom, cognom, nota);<br />
El procés d'escriure és exactament igual:<br />
while (fscanf(f, "%d %s %s %f",&codi, nom,cognom, ¬a) != EOF)<br />
printf("%d %s %s %f\n",codi, nom, cognom, nota);<br />
Mentre que la funció fscanf() no trobi el final de l'arxiu, es va llegint la informació en grups de<br />
quatre dades. Després s'escriu per pantalla el contingut de les variables.<br />
14
5.2.4 L’accés directe<br />
En aquest punt parlarem del que es coneix com accés directe. L’accés directe consisteix en indicar<br />
la posició a la que volem accedir. Aquesta posició s’indica en bytes. Les principals funcions per a<br />
l’accés directe són: fseek i ftell.<br />
fseek<br />
Aquesta funció ens ajudarà a situar-nos en una posició donada dins el fitxer.<br />
on:<br />
int fseek(FILE *stream, long offset, int origen)<br />
• stream és el punter al fitxer que estem utilitzant.<br />
• offset és el nombre de bytes que ens anem a desplaçar.<br />
• origen és el punt de partida del desplaçament. L’origen pot tenir tres valors, els quals es<br />
mostren a la següent taula.<br />
Si ha anat bé, la funció retorna 0. Altrament retorna diferent de 0.<br />
A continuació anem a veure una altra funció que ens retorna la posició en la que ens trobem dins el<br />
fitxer.<br />
ftell<br />
Aquesta funció retorna el punter actual del fitxer i l’utilitzarem per a obtenir la posició actual d’un<br />
fitxer. El desplaçament es mesura en bytes i des de l’inici del fitxer. Normalment podem utilitzar el<br />
valor que ens retorna l’ftell per cridar a la funció fseek.<br />
long int ftell(FILE *stream)<br />
La funció ftell retorna la posició en octets en la qual ens trobem dins el fitxer.<br />
Podem utilitzar aquesta funció per a conèixer la longitud exacta del fitxer:<br />
fseek(f,0,SEEK_END);<br />
l=ftell(f)<br />
15
Creeu un arxiu anomenat m5p04.c amb el següent contingut<br />
//m5p04.c - Trobar la mida d'un arxiu -<br />
#include <br />
int mida(char *);<br />
int main(int argc, char *argv[]){<br />
printf("mida de %s:%d\n",argv[1],mida(argv[1]));<br />
system ("pause");<br />
}<br />
int mida(char *arxiu){<br />
FILE *f;<br />
if ((f=fopen(arxiu,"rb"))==NULL){<br />
printf("error, no s'ha pogut obrir l'arxiu\n");<br />
return 0;<br />
}<br />
fseek(f, 0,SEEK_END);<br />
return ftell(f);<br />
}<br />
Explicació del programa<br />
La funció main() d'aquest programa rep un argument a través de la línia de comandament. No es fa<br />
cap comprovació del nombre d'arguments per tal de facilitar la lectura del codi, no obstant, seria<br />
interessant fer-la. Aquest argument serà el nom de l'arxiu, la mida del qual volem saber. La mida es<br />
troba a partir de la funció mida(). Aquesta funció té com únic argument el nom de l'arxiu. Aquesta<br />
funció s'encarrega d'obrir-lo. A continuació, amb:<br />
fseek(f, 0,SEEK_END);<br />
fa que el punter de posició de lectura de l'arxiu apunti a la darrera posició (0 posicions començant<br />
per l'última). Aleshores, la funció ftell() torna la posició d'aquest punter començant pel principi, és a<br />
dir, torna la mida de l'arxiu.<br />
16
Exercicis<br />
1. Treure els comentaris d’un arxiu font C/C++<br />
Escriu un programa que llegeixi un arxiu C/C++ i creï un de nou amb l’extensió .txt on les línies<br />
de comentari siguin eliminades.<br />
2. Encriptació d'un correu<br />
En Pere vol codificar els correus electrònics que envia als seus amics. Per aquest motiu va escriure<br />
una programa C/C++ per codificar-los i un altre per descodificar-los. Un cop comprovat el seu<br />
funcionament , va fer arribar a la resta d’amics el programa executable per poder llegir els<br />
missatges que ell els envia .<br />
En Pere fa arribar els missatges a traves d’un fitxer anomenat correu.txt i el seu contingut és un<br />
seguit de zeros ( 0 ) i uns ( 1 ). Per llegir el correu, cal agrupar-los de 8 en 8 i transformar aquest<br />
nombre binari en un nombre decimal. El seu corresponent codi ASCII és el caràcter que ens envia.<br />
El conjunt de caràcters a utilitzar corresponen als de la taula ASCII ( del 0 al 127 ).<br />
Exemple de les tres primeres lletres que trobaràs en el fitxer correu.txt :<br />
caràcter Binari decimal<br />
E 01000101 69<br />
l 01101100 108<br />
n 01101110 110<br />
Es demana que escriguis un programa C/C++ que et permeti llegir els correus que s’envien<br />
codificats amb aquest mètode i mostri el missatge en el monitor..<br />
A l'arxiu correu.txt tens un missatge de prova. Si el teu programa funciona correctament hauràs de<br />
llegir en la pantalla la següent dita:<br />
El negoci d'en Robert amb les cabres, que en donava dues negres per una de blanca.<br />
17
Nota: Per a treballar millor feu servir un codi semblant a aquest<br />
int main(){<br />
char c[]="01000101",*finalPtr;<br />
int a =strtol(c,&finalPtr,2);<br />
printf("%c",a);<br />
system("pause");<br />
}<br />
La funció strtol ens transformarà la cadena del primer paràmetre en un número interpretant-lo en la<br />
base que li diem en el tercer paràmetre (en aquest cas 2). No us preocupeu gaire d’entendre el segon<br />
paràmetre poseu-lo tal qual i ja està.<br />
Si voleu més informació mireu http://c.conclase.net/librerias/funcion.php?fun=strtol<br />
3. Ordenació<br />
Tenim dos fitxers A.txt i B.txt. Cada un d’ells conté un número indeterminat de nombres enters de<br />
forma ordenada. Escriviu un programa que escrigui a un fitxer C.txt tots els enters que hi ha en els<br />
dos fitxers de forma ordenada. Si algun valor surt repetit només cal ficar-ho una vegada<br />
18
Pràctica<br />
Generalitat de Catalunya<br />
Departament d’Educació<br />
IES SEP Mare de Déu de la Mercè<br />
CICLE: DESENVOLUPAMENT<br />
D’APLICACIONS INFORMÀTIQUES<br />
CRÈDIT: C5<br />
PROGRAMACIÓ ESTRUCTURADA I MODULAR<br />
CURS:<br />
1r<br />
DATA:<br />
05-05-10<br />
Nom i Cognoms: Nota:<br />
Degut a les dificultats de la policia per identificar els criminals, fa temps es va decidir substituir<br />
els dibuixants tradicionals per un programa informàtic que crea un retrat robot a partir d’una<br />
descripció del delinqüent. Actualment es disposa de milers de fitxers de text que contenen<br />
aquestes descripcions. Un individu es descriu amb 5 línies de text corresponents a les 5 línies<br />
del retrat robot. A cada línia s’apunta el nombre de vegades que apareix cada caràcter seguit del<br />
caràcter en qüestió, tal i com es pot veure en el següent exemple:<br />
Es demana fer un programa que a partir de la descripció d’un criminal que llegirem d’un fitxer<br />
de text, mostri el retrat robot per pantalla.<br />
19
5.3 Fitxers binaris<br />
La llibreria estàndard d'entrades i sortides de C proporciona dues funcions que permeten llegir i<br />
escriure blocs sencers de dades. Aquestes funcions són fread() i fwrite() i tenen els següents<br />
protocols:<br />
size_t fread (void *buffer, size_t mida, size_t numelements, FILE *arxiu);<br />
size_t fwrite (void * buffer, size_t mida, size_t numelements, FILE * arxiu);<br />
Aquestes funcions són especialment interessants per emmagatzemar o recuperar estructures<br />
senceres i no camp per camp.<br />
La funció fread llegeix numelements ítems de mida bytes del fitxer arxiu i els emmagatzema al<br />
buffer. El punter associat al fitxer s’incrementa amb el nombre de bytes llegits. fread retorna el<br />
nombre d’ítems llegits. Aquest nombre pot ser menor del que s’ha especificat a la seva crida si s’ha<br />
arribat a la fi del fitxer o ha donat un error.<br />
La funció fwrite escriu numelements ítems al fitxer arxiu, de mida bytes cadascun. Aquests ítems<br />
estan al buffer. El punter associat al fitxer s’incrementa amb el nombre de bytes escrits. fwrite<br />
retorna el nombre d’ítems escrits. Aquest nombre pot ser menor del que s’ha especificat a la seva<br />
crida si s’ha arribat a la fi del fitxer o ha donat un error.<br />
Presentarem dos programes, el primer dels quals permetrà emmagatzemar en un arxiu de disc<br />
registres d'una base de dades d'alumnes i notes. L'accés a l'arxiu es farà de registre en registre i no<br />
de camp en camp. Els valors dels diferents camps s'introduiran per teclat. Per finalitzar, l'entrada de<br />
dades s'haurà d'introduir el valor -1 en el camp codi.<br />
El segon dels programes permetrà llegir registres d'un arxiu i els mostrarà per pantalla. De la<br />
mateixa forma que el primer programa, l'accés a l'arxiu es farà de registre en registre.<br />
20
Creeu un nou arxiu anomenat m5p05.cpp i escriviu el següent codi:<br />
//m5p05.cpp - Escriptura de registres en disc -<br />
#include <br />
#include <br />
typedef struct fitxa{<br />
int codi;<br />
char nom[20];<br />
char cognom[20];<br />
float nota;<br />
};<br />
int main(){<br />
FILE *fp;<br />
fitxa alumne;<br />
char arxiu[12];<br />
printf("Introduiu el nom de l'arxiu de dades\n");<br />
scanf("%s",arxiu);<br />
if((fp=fopen(arxiu, "wb"))==NULL)<br />
printf("Error al intentar obrir l'arxiu\n");<br />
else<br />
do {<br />
printf("\nIntroduiu el codi...");<br />
scanf(" %d",&alumne.codi);<br />
if(alumne.codi!=-1) {<br />
printf("\nIntroduiu el nom... ");<br />
scanf(" %s",alumne.nom);<br />
printf("\nIntroduiu el cognom...");<br />
scanf(" %s", alumne.cognom);<br />
printf("\nIntroduiu la nota...");<br />
scanf(" %f",&alumne.nota);<br />
fwrite(&alumne,sizeof(alumne),1,fp);<br />
}<br />
}while (alumne.codi!=-1);<br />
fclose(fp);<br />
}<br />
21
Explicació del programa<br />
Els registres que emmagatzemarem seran estructures del tipus fitxa definida com a estructura<br />
global. Es defineix una variable d'aquesta estructura anomenada alumne. Aquesta variable ens<br />
servirà de buffer per escriure tot el registre de cop a l'arxiu.<br />
Per tal de finalitzar l'entrada de dades s'ha posat una comprovació darrere de l'entrada del camp<br />
codi. Si el valor introduït és igual a -1, se surt del bucle, es tanca l'arxiu i acaba el programa.<br />
Una vegada introduïts els quatre camps del registre s'emmagatzema de cop a l'arxiu amb la funció<br />
fwrite():<br />
fwrite(&alumne,sizeof(alumne),1,fp);<br />
Aquesta línia es pot llegir com: escriu a l'arxiu apuntat per la variable fp, 1 bloc de 48 octets<br />
(sizeof(alumne)=48) començant per l'adreça de memòria apuntada pel punter alumne.<br />
El següent programa mostra com es pot llegir els registres sencers de l'arxiu creat amb el programa<br />
anterior.<br />
Creeu un nou arxiu anomenat m5p06.cpp i escriviu el següent codi:<br />
//m5p06.cpp - Lectura de registres en disc -<br />
#include <br />
#include <br />
typedef struct fitxa{<br />
int codi;<br />
char nom[20];<br />
char cognom[20];<br />
float nota;<br />
};<br />
int main(){<br />
FILE *fp;<br />
fitxa alumne;<br />
char arxiu[12];<br />
printf("Introduiu el nom de l'arxiu de dades\n");<br />
scanf("%s",arxiu);<br />
if((fp=fopen(arxiu, "rb"))==NULL)<br />
printf("Error al intentar obrir l'arxiu\n");<br />
else{<br />
while (fread(&alumne,sizeof(alumne),1,fp))<br />
printf("%d %20s %20s %.2f\n",alumne.codi,alumne.nom,alumne.cognom,<br />
alumne.nota);<br />
fclose(fp);<br />
}<br />
system("pause");<br />
}<br />
22
Explicació del programa<br />
Aquest programa és realment similar a l'anterior. La lectura de dades del disc es fa amb la funció<br />
fread() de la següent forma:<br />
fread(&alumne,sizeof(alumne),1,fp);<br />
Aquest línia pot llegir-se com: llegeix a l'arxiu apuntat per fp 1 bloc de 48 octets i escriu aquests<br />
octets a la memòria començant per l'adreça apuntada pel punter alumne.<br />
La funció fread() retorna el nombre d'elements llegits. Aquest pot ser més petit que el tercer<br />
argument ( int nombre_de_elements ) en cas d'error o bé si em arribat a final del arxiu.<br />
En el nostre exemple si la funció fread() retorna un valor false ( zero ) ens indicarà que em arribat a<br />
final del arxiu de dades.<br />
Com pot passar en molts casos ens pot interessar carregar tot el fitxer en una taula. Això<br />
naturalment es pot fer llegint registre a registre en un bucle però també ho podem fer en una sola<br />
instrucció si sabem quants elements tenim. Per exemple i suposant que tenim la variable<br />
fitxa alumne[100]; :<br />
fseek(fp, 0, SEEK_END);<br />
nombreRegistres = ftell(fp)/sizeof(fitxa); // Calculem quants registres tenim<br />
rewind(fp); //Funció que rebobina per tornar a principi de fitxer<br />
fread(&alumne,sizeof(fitxa),nombreRegistres,fp); //Carreguem directament tot el fitxer a la taula<br />
23
Exercici<br />
1. Volem dissenyar un programa que ens permeti la gestió d’una senzilla agenda, a la qual<br />
figuren nom i número de telèfon de diferents persones.<br />
El programa ha de realitzar les següents tasques:<br />
a) Entrada de dades: nom i telèfon<br />
b) Buscar el número de telèfon d’una persona determinada<br />
c) Eliminar les dades d’una persona<br />
d) Modificar el número de telèfon d’una persona determinada<br />
e) Llistar totes les dades<br />
Aquestes tasques no s’han de realitzar seqüencialment, sinó a voluntat de l’usuari, és a dir el<br />
programa ha de presentar el conjunt de tasques que pot realitzar i l’usuari triarà una d’elles.<br />
Finalitzada aquesta tasca, el conjunt d’opcions ha d’estar disponible novament.<br />
Consideracions: La tàctica per eliminar un registre hauria de ser la següent es va llegint<br />
del fitxer i es va gravant en un fitxer auxiliar; quan trobem el registre que volem eliminar<br />
simplement no el gravem a l’altre fitxer. Al final cal eliminar el fitxer original i reanomenar<br />
l’auxiliar amb el nom del fitxer original :<br />
remove (“agenda.dat”);<br />
rename (“auxiliar.dat”, “agenda.dat”);<br />
A l’hora de modificar s’ha de considerar que després de llegir un registre, el fitxer ha mogut<br />
el marcador fins el següent registre i per tant ens caldrà retrocedir una posició.<br />
24
Pràctica<br />
Generalitat de Catalunya<br />
Departament d’Educació<br />
IES SEP Mare de Déu de la Mercè<br />
CICLE: DESENVOLUPAMENT<br />
D’APLICACIONS INFORMÀTIQUES<br />
CRÈDIT: C5<br />
PROGRAMACIÓ ESTRUCTURADA I MODULAR<br />
CURS:<br />
1r<br />
DATA:<br />
07-06-10<br />
Nom i Cognoms: Nota:<br />
Volem gestionar una petita base de dades a la qual tenim: categories i els productes d’aquestes<br />
categories. La informació que guardem és la següent:<br />
Categories - Cod_cat : autonumèric<br />
- Nom_cat: string<br />
Productes - Cod_prod : autonumèric<br />
- Nom_prod: string<br />
- Cod_cat : clau forana<br />
Els dos fitxers han d’estar sempre ordenats pel codi i no ha d’haver-hi cap salt de codi ( és a dir si hi<br />
ha n registres, els codis aniran de 1 a n).<br />
El programa ha de permetre les següents opcions:<br />
1. Introduir una nova categoria: Se’ns dóna un nom, si no existeix se li assigna el valor de<br />
l’últim codi +1 i es grava.<br />
2. Eliminar una categoria: se’ns dóna un nom i l’esborrem. Tots els codis de les categories<br />
posteriors han de ser reassignats i per tant també els codis de categoria dels seus productes.<br />
Tots els productes de la categoria eliminada han de ser també eliminats i per tant també cal<br />
la reassignació de codis.<br />
3. Consulta per categoria: se’ns dóna un nom de categoria i llistem tots els seus productes.<br />
4. Llistat de categories.<br />
5. Introduir un nou producte: se’ns dóna un nom i un codi de categoria. Si la categoria existeix<br />
i el producte no, se li assigna el codi pertinent i es grava.<br />
6. Eliminar un producte: se’ns dóna un nom i l’esborrem. Cal fer la reassignació de codis.<br />
7. Llistat de productes amb la seva categoria.<br />
A l’hora d’entregar la pràctica cal que els fitxers tinguin prou dades per poder provar el<br />
funcionament en condicions. Està totalment prohibit l’ús de taules per emmagatzemar les dades<br />
dels fitxers i a més :<br />
- Cal fer un bon disseny descendent.<br />
- Cal declarar les mínimes variables globals.<br />
- Les tuples cal declarar-les amb typedef.<br />
- Cal comentar el codi<br />
25