20.04.2013 Views

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

SHOW MORE
SHOW LESS

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",&nota);<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, &nota) != 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, &nota) != 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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!