Ministerul Educaţiei Republicii Moldova Universitatea Tehnică a Moldovei Facultatea Calculatoare, Informatică şi Microelectronică. Departamentul de Informatică şi Ingineria sistemelor Lucrare de an La disciplina: “Structuri de date şi algoritmi”. Tema: “Realizarea unei Baze de Date cu ajutorul limbajului C”. A efectuat : st.gr. A verificat : Chişinău, 2018 Cuprins 1. Introducere.............................................................................3 2. Liste…………........................................................................5 3. Liste circulare simplu inlantuite............................................10 4. Tipuri de fisiere.....................................................................14 5. Lucrul cu fişierele.................................................................15 6. Citirea si scrierea fisierelor...................................................17 7. Stergerea, redenumirea si crearea fisierelor..........................21 8. Listingul programului...........................................................22 9. Rezultatul final……..............................................................32 10. Concluzii............................................................................34 11. Bibliografie .......................................................................35 1 1.Introducere Scrierea unui program intr-un limbaj de programare este doar primul pas dintr-un proces care mai cuprinde si alti pasi. Mai corect ar fi sa spunem scrierea unei versiuni initiale a programului, pentru ca intotdeauna aceasta forma initiala este corectata, modificata sau extinsa pentru eliminarea unor erori, pentru satisfacerea unor noi cerinte sau pentru imbunatatirea performantelor in executie. Un program scris intr-un limbaj independent de masina (C, Pascal, s.a.) trebuie mai intai tradus de catre un program translator sau compilator. Compilatorul citeste si analizeaza un text sursa (de exemplu in limbajul C) si produce un modul obiect (scris intr-un fisier), daca nu s-au gasit erori in textul sursa. Pentru programele mari este uzual ca textul sursa sa fie format din mai multe fisiere sursa, care sa poata fi scrise, compilate, verificate si modificate separat de celelalte fisiere sursa. Mai multe module obiect, rezultate din compilari separate sunt legate impreuna si cu alte module extrase din biblioteci de functii standard intr-un program executabil de catre un program numit editor de legaturi (“Linker” sau “Builder”). Executia unui program poate pune in evidenta erori de logica sau chiar erori de programare care au trecut de compilare (mai ales in limbajul C). Cauzele erorilor la executie sau unor rezultate gresite nu sunt de obicei evidente din cauza ca ele sunt efectul unui numar mare de operatii efectuate de calculator. Pentru descoperirea cauzelor erorilor se poate folosi un program depanator (“Debugger”) sau se pot insera intructiuni de afisare a unor rezultate intermediare in programul sursa, pentru trasarea evolutiei programului. Fazele de modificare (editare) a textului sursa, de compilare, linkeditare si executie sunt repetate de cate ori este necesar pentru a obtine un program corect. De fapt, testarea unui program cu diverse date initiale poate arata prezenta unor erori si nu absenta erorilor, iar efectuarea tuturor testelor necesare nu este posibila pentru programe mai complexe (pentru. un compilator sau un editor de texte, de exemplu). Programele compilator si linkeditor pot fi apelate in mod linie de comanda sau prin selectarea unor optiuni din cadrul unui mediu integrat de dezvoltare a programelor (IDE = Integrated Development Environment). Alte programe utilizate in procesul de dezvoltare a unor aplicatii mari sunt: - program bibliotecar pentru crearea si modificarea unor biblioteci de subprograme pe baza unor module obiect rezultate din compilare. - program pentru executia unor fisiere de comenzi necesare pentru compilarea selectiva si re-crearea programului executabil, dupa modificarea unor fisiere sursa sau obiect (“make”). - program de control al versiunilor succesive de fisiere sursa. Limbajul C s-a impus în principal datoritã existentei unui standard care contine toate facilitãtile necesare unui limbaj pentru a putea fi folosit într-o mare diversitate de aplicatii, fãrã a fi necesare abateri sau extinderi fatã de standard 2 (cazul limbajului Pascal). Un exemplu este recunoasterea posibilitãtii ca un program sã fie format din mai multe fisiere sursã si a compilãrii lor separate, inclusiv referiri dintr-un fisier în altul. In plus, existã un numãr relativ mare de functii uzuale care fac parte din standardul limbajului si care contribuie la portabilitatea programelor C. Limbajul C permite un control total asupra operatiilor realizate de procesor si asupra functiilor sistemului de operare gazdã, aproape la fel ca si limbajele de asamblare. Astfel se explicã de ce majoritatea programelor de sistem si utilitare sunt scrise de mai multi ani în limbajul C, pe lângã multe programe de aplicatii. Limbajul C permite scrierea unor programe foarte compacte, ceea ce poate fi un avantaj dar si un dezavantaj, atunci când programele devin criptice si greu de înteles. Scurtarea programelor C s-a obtinut prin reducerea numãrului de cuvinte cheie, prin existenta unui numãr mare de operatori exprimati prin unul sau prin douã caractere speciale dar si prin posibilitatea de a combina mai multi operatori si expresii într-o singurã instructiune (acolo unde alte limbaje folosesc mai multe instructiuni pentru a obtine acelasi efect). Din perspectiva timpului se poate spune cã instructiunile C sunt o reusitã a limbajului (si au fost preluate fãrã modificari de multe alte limbaje : C++, Java s.a.) dar functiile de intrare-iesire (printf,scanf) nu au fost un succes (si au fost înlocuite în alte limbaje). Un alt neajuns s-a dovedit a fi necesitatea argumentelor de tip pointer pentru functiile care trebuie sã modifice o parte din argumentele primite si a fost corectat prin argumente de tip referintã. Utilizarea directã de pointeri (adrese de memorie) de cãtre programatorii C corespunde lucrului cu adrese de memorie din limbajele de asamblare si permite operatii imposibile în alte limbaje, dar în timp s-a dovedit si o sursã importantã de erori la executie, greu de depistat. Au mai fost preluate în limbajele post-C si anumite conventii, cum ar fi diferenta dintre litere mici si litere mari, diferenta dintre caractere individuale si siruri de caractere (si terminarea sirurilor de caractere cu un octet zero), operatorii, comentariile s.a. 3 2. Liste Aspecte teoretice. Definiţie. Operaţii asupra listelor O listă L e o secvenţă de zero sau mai multe elemente, numite noduri, toate fiind de acelaşi tip de baza T. L=a1,a2,...,an (n>=0) Dacă n>=1, a1 se spune că este primul nod al listei, iar an, ultimul nod. Daca n=0, lista este vida. O proprietate importantă a unei liste este aceea că nodurile sale pot fi ordonate liniar funcţie de poziţia lor în cadrul listei. Se spune că ai precede pe ai+1 (i=1,2,...,n-1), iar ai succede pe ai-1 (i=2,3,...,n), ai aflându-se pe poziţia i. Se postulează(presupune) existenta poziţiei următoare ultimului element al listei şi se introduce funcţia FIN(L) ce va returna poziţia următoare poziţiei n din lista L de n elemente. Folosind notaţiile anterioare şi notând x(de tip T) un nod al listei, iar p fiind de tip poziţie, se introduce următorul set reprezentativ de operatori aplicabili obiectelor de tip lista: INSEREAZA(L,x,p)- inserează în lista L nodul x, în poziţia p; dacă L=a1,a2,...,an, în urma inserţiei: p<FIN(L),L=a1,...,ap-1,x,a p+1,...,an p=FIN(L),L=a1,...,an,x p>FIN(L),rezultatul inserţiei este imprevizibil. Implementarea listelor . Structuri recursive de tip listă Cu ajutorul tipului pointer, se defineşte structura unui nod al listei liniare care apare ca o structură recursivă, având o componentă de tip identic cu al structurii complete. type PointerNod=^Nod; Nod=record cheie:TipCheie; urmator:PointerNod; info:TipInfo end; var început:PointerNod; Caracteristica unei astfel de structuri constă în prezenta unei singure înlănţuiri. Câmpul cheie serveşte la identificarea nodului( acest câmp poate face parte din informaţia utilă, el este utilizat în cazul căutărilor, sortării…), câmpul următor e pointer de înlănţuire la nodul următor, iar cel info conţine informaţia utilă. Variabila început indica spre primul nod al listei; în unele situaţii în locul lui început se utilizează un nod fictiv, adică o variabila de tip nod cu câmpurile cheie şi info neprecizate, dar câmpul următor indicând spre primul nod al listei. De asemenea uneori e util a se păstra pointerul spre ultimul nod al listei. 4 O varianta este a listelor circulare la care dispare noţiunea de prim, ultim nod. Tehnici de inserţie a nodurilor şi de creare a listelor înlănţuite a)inserţia unui nod la începutul listei Dacă început e variabila pointer ce indica spre primul nod al listei, iar q o variabila auxiliara de tip pointer, secvenţa următoare realizează inserţia la începutul listei şi actualizează pointerul început: new(q); {creează spaţiu pentru un nou nod} q^.urmator:=inceput; {asignarea câmpurilor cheie şi info} inceput:=q; Secvenţa e corectă şi pentru inserţia într-o listă vidă, caz în care inceput=nil (nil fiind pointerul vid, care nu se refera la nici o variabilă indicată). b)inserţia unui nod la sfârşitul listei Devine mai simplă dacă se păstrează o variabilă sfârşit indicând spre ultimul nod al listei: new(q); {creează spatiu pentru noul nod ultim al listei} sfirsit^.urmator:=q; q^.urmator:=nil; {asignarea câmpurilor cheie şi info} sfirsit:=q; Pentru inserţia la sfârşitul listei e necesara existenta a cel puţin un nod, care se creează prin procedura de la paragraful anterior. c)inserţia unui nod după unul indicat (p) E simplă pentru că se cunoaşte pointerul spre nodul anterior şi spre cel următor celui ce se inserează (pointerul spre nodul următor e valoarea câmpului următor al nodului indicat). new(q); q^.urmator:=p^.urmator; {legăm nodul de nodul anterior} p^.urmator:=q; {legăm nodul anterior de q} d)inserţia unui nod în fata unui nod indicat Printr-un artificiu, se reduce acest caz la cel anterior: se inserează un nod după cel indicat, cheia şi informaţia din nodul indicat fiind atribuite noului nod inserat şi fiind înlocuite cu valorile nodului ce trebuia inserat. Tehnici de suprimare a) Suprimarea nodului următor celui indicat de o variabila pointer q se face astfel: p:=q^.urmator; {punem un indicator către nodul care urmează a fi şters} 5 q^.urmator:=q^.urmator^.urmator;{nodul anterior celui ce urmează a fi şters pointează către nodul următor acestuia} dispose(p);{ eliberăm memoria ocupată de nod} Pentru a elibera memoria ocupată de nodul care va fi şters mai avem nevoie de un pointer spre acest nod. După ce s-a făcut ruperea legăturilor se eliberează memoria cu dispose. b) Suprimarea nodului indicat de o variabilă pointer q se face astfel: p:=q^.urmator;{se memorează legătura către nodul următor} q^:=q^.urm^;{se copiază conţinutul nodului următor peste cel curent} dispose(p);{se dezalocă memoria} Această metodă nu poate fi utilizată pentru ultimul nod din listă. Traversarea unei liste înlănţuite Dacă nodul de început al listei e indicat de variabila început, o variabila auxiliara q, care parcurge toate nodurile listei până când valoarea ei devine nil, permite accesul la fiecare nod şi efectuarea operaţiei specifice traversării. Aplicatii ale listelor înlănţuite Liste ordonate şi reorganizarea listelor a)Căutarea intr-o lista neordonată; tehnica fanionului. Se considera o lista simplu înlănţuita, cu nodurile de tip Nod. Daca început indica spre primul nod al listei, iar ordinea cheilor în lista este aleatoare, căutarea unei chei implica traversarea listei. Funcţia booleana găsit returnează valoarea true şi pointerul spre nodul cu cheia egala cu cea căutată, dacă un astfel de nod există şi valoarea false în caz contrar: function gasit(val:TipCheie;var poz:PointerNod):boolean; var found:boolean; begin poz:=inceput;found:=false; while (poz<>nil) and not found do if poz^.cheie=val then found:=true else poz:=poz^.urmator; gasit:=found end; Căutarea se poate perfecţiona prin utilizarea metodei fanionului, lista prelungindu-se cu un nod fictiv numit fanion, la creare lista conţinând acest unic 6 nod. În funcţia gasit, înainte de baleierea listei, informaţia căutată se introduce în cheia nodului fanion, astfel încât va exista cel puţin un nod cu cheia căutată: var fanion:PointerNod; ... function gasit(val:TipCheie;var poz:PointerNod):boolean; begin poz:=inceput;fanion^.cheie:=val; while poz^.cheie<>val do poz:=poz^.urmator; gasit:=poz<>fanion end; b)Crearea unei liste ordonate; tehnica celor doi pointeri în continuare se prezintă o metodă foarte simpla pentru crearea unei liste ordonate, tipurile PointerNod şi Nod fiind cele definite anterior. Lista se iniţializează cu doua noduri fictive pointate de doua variabile pointer, inceput şi fanion: var inceput, fanion:PointerNod; procedure init; begin new(inceput); new(fanion); inceput^.urmator:=fanion end; Pentru introducerea unei noi chei în listă, păstrând ordonarea, se va scrie o funcţie gasit, care dacă găseşte cheia în listă returnează valoarea true şi pointerii p1 spre nodul gasit şi p2 spre cel anterior, respectiv în cazul negasirii cheii, valoarea false şi pointerii p1 şi p2 spre nodurile intre care trebuie făcută inserţia: function gasit(val:TipCheie;var p1,p2:TipPointer):boolean; begin p2:=inceput; p1:=p2^.urmator; fanion^.cheie:=val; while p1^.cheie<val do begin p2:=p1; p1:=p2^.urmator end; gasit:=(p^.cheie=val) and (p1<>fanion) end; 7 Fragmentul de program ce inserează o noua cheie este: var p1,p2,p3:PointerNod;val:TipCheie; ... if not gasit(val,p1,p2) then begin new(p3); p2^.urmator:=p3; with p3^ do begin cheie:=val; {info} urmator:=p1 end end; Pentru o tipărire a cheilor dintr-o listă ordonată astfel creată, pointerul ce parcurge nodurile trebuie sa fie iniţializat cu valoarea pointerului spre primul nod efectiv al listei, următor celui început, iar parcurgerea listei se face până la întâlnirea nodului fanion: var p:PointerNod; ... p:=inceput^.urmator; while p<>fanion do begin {prelucrare nod} p:=p^.urmator end; c)tehnica de căutare în listă cu reordonare În compilatoare, structurile de date de tip listă liniară sunt foarte avantajoase în crearea şi exploatarea listei identificatorilor. Conform principiului localizării, apariţia unui identificator oarecare în textul sursa, poate fi urmata cu mare probabilitate de una sau mai multe reapariţii. Pornind de la acest principiu, s-a conceput metoda de căutare cu reordonare, care consta în aceea ca ori de câte ori un identificator se caută şi se găseşte în listă, el se aduce la începutul listei, astfel încât la proxima apariţie, deci căutare, el se va găsi la începutul listei, iar dacă nu s-a găsit se inserează la începutul listei. 8 3. Liste circulare simplu inlantuite O lista circulara simplu inlantuita este o lista in care ultimul element contine campul ce adreseaza elementul urmator, adresa primului element. 1.1. Definirea unei liste circulare simplu inlantuite Type lista = ^nod; nod = record inf: integer; adr: lista; end; var pr: lista; inf1 adr2 inf2 adr3 inf3 adr1 Crearea unei liste circulare se realizeaza in mod asemanator cu o lista liniara simplu inlantuita, cu deosebirea ca ultimul element adaugat in lista nu va mai avea in campul de adresa valoarea NIL, ci adresa primului element adaugat. 1.2. Crearea unei liste circulare cu numar cunoscut de elemente Vom crea mai intai primul element al listei. Folosind un ciclu for vor fi adaugate la sfarsitul listei celelalte elemente. In final campul de adresa al ultimului element adaugat va contine adresa primului element. Vom utiliza pointerii : pr – contine adresa primului element adaugat in lista; ul – contine adresa ultimului element adaugat in lista; p – contine adresa elementului ce se adauga. Procedure creare (var p:lista); Var p, ul : lista; i: integer; begin new(pr); readln(pr^.inf); ul:=pr; for i := 1 to n-1 do begin new(p); readln(p^.inf); ul^.adr:=p; ul:=p; 9 end; ul^.adr:pr; end; 1.3. Afisarea elementelor unei liste circulare Parcurgem nodurile listei cu ajutorul unui pointer care plecand de la un element al listei, va referi pe rand fiecare nod al listei, pana cand va adresa nodul de pornire. Parcurgerea folosind un ciclu while Procedure parc1 (pr:lista); var p:lista; begin p:=pr; while (p^.adr<>pr) do begin write (p^.inf,’ ’); p:= p^.adr; end; write (p^.inf); end; Parcurgerea folosind un ciclu repeat Procedure parc2 (pr:lista); var p:lista; begin p:=pr; repeat write (p^.inf,’ ’); p:=p^.adr; until p=pr; end; 1.4. Adaugarea unui element intr-o lista circulara Sa se scrie un subprogram ce realizeaza inserarea unui nod dupa elementul de cheie k dintr-o lista circulara. Observam ca intr-o lista circulara putem accesa toate elementele listei pornind din orice nod al acesteia. Avand in vedere acest lucru, pentru a evita tratarea cazului particular de inserare inainte primului element, vom parcurge lista prin intermediul lui pr, salvand adresa primului element intr-o variabila p. Procedure adaug (var pr:lista; k:integer); var p: lista; begin 10 p:=pr: repeat if pr^.inf=k then begin new(q); readln(q^.inf); q^adr:= pr^.adr; pr:=p; end; else pr:=pr^.adr; until (pr=p); end; 1.5. Eliminarea elementelor dintr-o lista circulara Sa se scrie un subprogram ce realizeaza eliminarea elementelor de cheie para dintr-o lista circulara. In situatia in care toate elementele sunt pare lista devine vida. Punem in evidenta acest lucru prin pr:=nil. Parcurgem lista prin pr, pozitionandu-ne pe elementul anterior care trebuie sters. Procedure elimin (var pr:lista); var p,q:lista; begin p:=pr; while pr^.adr<>p do if pr^.adr^.inf mod 2 = 0 then begin {se elimina pr^.adr} q:=pr^.adr; pr^.adr:=q^.adr; dispose(q); end else pr:=pr^.adr; if p^.adr=pr then begin {lista are un singur element} if pr^.inf mod 2 = 0 the begin dispose(pr); pr:=nil; end end else {se verifica daca nodul de pornire este par, situatie in care se elimina} pr^.inf mod 2 = 0 then begin dispose(p); end; end; 11 1.6. Crearea unei liste circulare cu numar necunoscut de elemente Se sa fisierul Numere.in. Sa se creeze o lista circulara simplu inlantuita ce contine numerele pare din fisier. type lista = ^nod; nod = record inf:integer; adr:lista; end; var pr:lista; f:text; procedure creare (var pr:lista); var p,ul:lista; k:integer; begin assign(f,’numere.in’); reset(f); pr:=nil; while not seekof(f) do begin read(f,k); if k mod 2=0 then begin new(p); p^.inf:=k; if pr=nil then begin pr:=p; ul:p; end else begin ul^.adr:=p; ul:=p; end; end; end; ul^.adr:= pr; close(f); end; procedure parc (pr:lista); var p:lista; begin p:=pr; repeat write (p^.inf, ’ ’); p:=p^.adr; until p=pr; end; begin {main} creare (pr); parc (pr); end. 12 4.Tipuri de fisiere Un fisier ("File") este o colectie de date memorate pe un suport extern si care este identificatã printr-un nume. Fisierele se folosesc fie pentru date initiale si pentru rezultate mai numeroase, fie pentru pãstrarea de duratã a unor date de interes pentru anumite aplicatii. Fisierele sunt entitãti ale sistemului de operare si ca atare ele au nume care respectã conventiile sistemului, fãrã legãturã cu un limbaj de programare. Operatiile cu fisiere sunt realizate de cãtre sistemul de operare, iar compilatorul unui limbaj traduce functiile (instructiunile) de acces la fisiere în apeluri ale functiilor sistem. Programatorul se referã la un fisier printr-o variabilã; tipul acestei variabile depinde de limbajul folosit si chiar de functiile utilizate (în C). Asocierea dintre numele extern (un sir de caractere) si variabila din program se face la deschiderea unui fisier, printr-o functie standard. De obicei prin "fisier" se subîntelege un fisier disc (pe suport magnetic sau optic), dar notiunea de fisier este mai generalã si include orice flux de date din exterior spre memorie sau dinspre memoria internã spre exterior. Dispozitivele periferice uzuale au nume de fisiere predefinite; de exemplu, în limbajul C sub MS-DOS si MS-Windows se pot folosi urmãtoarele nume : CON = consola sistem (tastaura la citire si monitor la scriere) PRN (LPT) = imprimanta sistem Pentru fisierele disc un nume de fisier poate include urmãtoarele: - Numele unitãtii de disc sau partitiei disc ( ex: A:, C:, D:, E:) - "Calea" spre fisier, care este o succesiune de nume de fisiere catalog (director), separate printr-un caracter ('\' în MS-DOS si MS-Windows, sau '/' în Unix si Linux) - Numele propriu-zis al fisierului ( max 8 litere si cifre în MS-DOS) - Extensia numelui, care indicã tipul fisierului (continutul sãu) si care poate avea între 0 si 3 caractere în MS-DOS). Exemple de nume de fisiere disc: A:bc.rar , c:\borlandc\bin\bc.exe c:\work\p1.cpp , c:\work\p1.obj Sistemele MS-DOS si MS-Windows nu fac deosebire între litere mari si litere mici, în cadrul numelor de fisiere, dar sistemele de tip Unix sau Linux fac deosebire între litere mari si litere mici. Consola si imprimanta sunt considerate fisiere text, adicã: - între aceste fisiere si memorie se transferã caractere ASCII - se recunoaste caracterul sfârsit de fisier (Ctrl-Z în MS-DOS si MSWindows, Ctrl-D în Unix) - se poate recunoaste la citire un caracter terminator de linie ('\n'). Un fisier text pe disc contine numai caractere ASCII, grupate în linii si este terminat printr-un caracter terminator de fisier (Ctrl-Z), adãugat automat la închiderea fisierului, dupã scriere în fisier. Functiile de citire sau de scriere numere 13 din/in fisiere text realizeazã conversia automatã din format extern (sir de caractere) în format intern (binary virgulã fixã sau virgulã mobilã) la citire si conversia din format intern în format extern, la scriere. Fisierele disc pot contine si numere în reprezentare internã (binarã) sau alte date ce nu reprezintã numere (de exemplu, fisiere cu imagini grafice, în diverse formate). Aceste fisiere se numesc fisiere binare, iar citirea si scrierea se fac fãrã conversie de format. Pentru fiecare tip de fisier binar este necesar un program care sã cunoascã si sã interpreteze corect informatiile binare din fisier. Fisierele disc trebuie deschise si închise, dar fisierele consolã si imprimanta nu trebuie deschise si închise. 5. Lucrul cu fişierele a)Deschiderea fisierelor – functia fopen ( ) FILE *fopen(char *nume_fisier, char *mod); Functia primeste in nume_fisier un pointer spre numele fisierului si un pointer, mod, spre un sir de caractere care defineste modul de acces si returneaza un pointer spre fisier, daca operatia a decurs normal sau NULL, daca fisierul nu se poate deschide. Acest pointer trebuie testat inainte de a trece la alte prelucrari pentru a ne asigura ca deschiderea s-a facut corect. Valorile pentru mod sunt date in tabelul alaturat. Valorile posibile care definesc modul de acces Mod – Semnificatie r - deschiderea unui fisier text pentru CITIRE (read) w - deschiderea unui fisier text pentru SCRIERE (write) a - deschiderea unui fisier text pentru ADAUGARE (append) dupa ultima intregistrare.Un fisier inexistent poate fi creat in modurile w sau a r# - citire si modificare (scriere) in mod text w# - deschidere pentru modificare in mod text a# - deschidere pentru modificare cu scriere la sfarsitul de fisier; mod text rb - deschidere pentru citire in mod binar wb - deschiderea unui fisier pentru modificare in mod binar ab - adaugare in mod binar r#b - deschidere fisier binar pentru citire/scriere w#b – deschidere fisier binar pentru scriere a#b – adaugare la un fisier binar 14 Exista constantele predefinite: FILENAME_MAX, care precizeaza lungimea maxima a numelui unui fisier, si FOPEN_MAX care precizeaza numarul maxim de fisiere ce pot fi deschise simultan. b) Inchiderea fisierelor – functia fclose ( ) int fclose(FILE *f); Functia primeste pointerul spre fisier f, obtinut cu functia fopen() si returneaza zero daca se incheie cu succes sau –1 in caz de eroare. Prin inchiderea fisierului se pierde conexiunea logica intre pointer si fisier si au loc operatiile: 1. scrierea datelor nescrise din bufferul de iesire in fisier (tehnica se foloseste pentru a mari eficienta accesului la fisiere deschise pentru scriere/actualizare, prin inlocuirea scrierii in fisier inregistrare cu inregistrare, cu scrierea intr-un buffer a mai multor inregistrari; la umplerea bufferului acesta fiind scris o singura data, automat, pe disc. Operatia se numeste "flushing the buffer" si poate fi apelata explicit cu functia: int fflush(FILE *f); in scopul de a elibera zona tampon a fisierului. 2. abandonarea datelor necitite din bufferul de intrare 3. eliberarea automata a bufferelor alocate fisierelor c) Reasignarea din program a fisierelor FILE *freopen(const char *nume_f,const char *mod, FILE *f); Efectul functiei este inchiderea fisierului pointat de pointerul f si deschiderea unui nou fisier cu numele precizat de nume_f in modul de acces dat de mod . Pointerul f va deveni noul pointer spre fisier. Astfel, prin intermediul aceluiasi pointer f putem avea acces la fisiere diferite, dar in momente de timp diferite 15 6. Citirea si scrierea fisierelor a) intrari/iesiri la nivel de caracter a1) int fgetc(FILE *f); int getc (FILE *f); Functia returnreaza codul ASCII al urmatorului caracter citit din fisierul f. Caracterul este convertit la intreg sau EOF in caz de eroare sau detectie de sfarsit de fisier. a2) int fputc(int c, FILE *f); int putc (int c, FILE *f); Scrie in fisierul f caracterul c ca un unsigned char . Se returneaza caracterul c sau EOF in caz de eroare. a3) int getchar(void); - citeste un caracter de la tastatura int getc(stdin); - citeste un caracter de la tastatura int puchar(int c); - scrie un caracter pe ecran int putc(int c, stdout); - scrie un caracter pe ecran b) intrari/iesiri la nivel de sir de caractere b1) char *fgets(char *s, int n, FILE *f); Citeste n-1 caractere din fisierul f in tabloul s.Citirea se opreste la detectarea codului '\n'.Caracterul '\n' se include in s ,apoi se adauga 'i0'. Se returneaza un pointer spre sirul s sau NULL in caz de eroare sau sfarsit de fisier. b2) char *gets(char *s); Citeste o linie de la tastatura in sirul de caractere s . Caracterul '\n' nu se include in s, dar la sfarsit se adauga 'i0'. b3) int fputs(const char *s, FILE *f); Se scrie sirul s in fisierul f. Se introduce EOF in caz de eroare sau o valoare pozitiva sau egala cu zero in rest. 16 b4) int puts(const char *s); - scrie sirul de caractere s pe ecran Exemplu: Un program care deschide fisierul de pe unitatea de disc c:, c:text.txt in care se va scrie un text citit de la tastatura. Fisierul se va inchide, apoi se va redeschide pentru a-l citi si afisa pe ecran. In final fisierul se inchide . #include<stdio.h> #incude<stdlib.h> void main(void) { FILE *f; char mesaj[60], c, *psir; printf("\n Intoduceti un mesaj de maxim 60 de caractere :ih"); gets (mesaj); // deschidem fisierul pentru scriere if ((f = fopen ("c:text.txt","w")) = = NULL){ printf ("\n Eroare la deschidere fisier"); exit (1); } // scriem in fisier psir = mesaj; while(*psir){ if(fputc(*psir++, f) == EOF){ printf ("\n Eroare la scriere in fisier"); exit (1); } } // inchidem fisierul fclose(f); // redeschidem fisierul pentru citire if ((f =fopen("c:text.txt", "r")) == NULL ) { printf ("\n Eroare deschidere fisier"); exit (1); } // citim fisierul si scriem pe ecran while((c = fgetc(f) != EOF) putchar(c); // inchidem fisierul fclose(f); } Se cere sa se rescrie programul folosind functiile fputs ( ) si fgets ( ) . 17 c) intrari/iesiri cu conversie de format c1) int fprintf( FILE *f, const char *format, .. ..); Scrie in fisierul f , conform cu formatul dat, un anumit numar de argumente. Numarul descriptorilor din format trebuie sa coincida cu numarul de argumente. Exemplu: int I=6; float r=6.0; fprintf(fis1, "%dit%f, i, r); /* scrie in fisierul pointat de fis1 un intreg, apoi un tab si apoi un real */ c2) int fscanf(FILE *f, const char *format,.. ...); Citeste date din fisierul f sub controlul specificatorului de format si atribuie valorile argumentelor. Argumentele trebuie sa fie obligatoriu pointeri. Citirea se termina la epuizarea sirului care precizeaza specificatorul de format . Exemplu: fscanf(fis2, "%f%d", &v, &i); citeste din fis2 doua variabile: prima reala (%f), a doua intreaga (%d). d) intrari/iesiri pentru date binare Desi functiile fprintf( ) si fscanf sunt foarte utile pentru ca permit scrierea/citirea datelor sub controlul unui format, ele au dezavantajul ca la transfer realizeaza conversia din format intern in format ASCII si invers. Aceasta conversie duce la cresterea timpului de acces la informatiile din fisiere scazand viteza de prelucrare. Fisierele create cu aceste functii vor fi mai mari decat cele create cu functii in care datele sunt scrise in format intern . Functiile fread( ) si fwrite( ) permit citirea si scrierea fara nici o conversie si fara a face vreo interpretare a datelor. Astfel, fisierul va fi exact imaginea datelor din memorie. Se utilizeaza notiunea de articol pentru a referi o variabila simpla sau compusa de lungime fixa. d1) unsigned fread(void *p,unsigned dim, unsigned nr_artic, FILE *f) ; Citeste un numar de nr_artic articole, fiecare de lungime dim octeti, din fisierul f intr-o zona de memorie pointata de pointerul p. Functia returneaza numarul de articole citite corect sau –1 in caz de eroare . 18 Observatii: - pointerul p trebuie sa pointeze spre un tip de variabila in concordanta cu tipul articolului sau catre o zona de memorie alocata cu malloc( ); - pentru a prelucra fisiere binare ele se vor deschide in modul b (modul binar): wb – creare rb – citire r#b – citire/scriere w#b – scriere , modificare a#b – adaugare Exemplu: fread(&i, 2, 1, fis); /* citeste din fisierul fis 1 articol de 2 octeti si-i depune in variabila i (intreg) */ p = malloc(4); fread(p, 4, 1, fis); /* citeste din fisierul fis 1 articol de 4 octeti si-i depune la adresa data de p (real) */ d2) unsigned fwrite(void *p, unsigned dim, unsigned nr_artic, FILE *f); Scriem in fisierul f un numar de nr_artic articole, fiecare de lungime dim octeti, articole preluate din zona de memorie pointata de p . 19 7. Stergerea , redenumirea si crearea fisierelor a) int remove(const char *nume_fis); int unlink(const char *nume_fis); Se sterge fisierul cu numele nume_fis si se returneaza 0 la realizarea cu succes a functiei sau diferit de zero 0 in caz de eroare. Exemplu: char nume_fis[12] printf("\nIntroduceti numele fis de sters:"); gets(nume_fis); remove(nume_fis); b) int rename(const char *nume_nou, const char *nume_vechi); Redenumeste fisierul specificat de nume_vechi cu un alt nume, nume_nou. c) FILE *tempfile(void); Se creeaza un fisier temporar in modul "wb#", care se va sterge automat la inchiderea sau terminarea normala a programului. Functia intoarce un pointer la fisier sau NULL in caz de eroare. Apeluri succesive vor deschide fisiere distincte. Functia este utilizata in special atunci cand se manevreaza un volum mare de date care nu incape in memorie si pentru care este necesara crearea unor fisiere de manevra pe disc. Aceste fisiere sunt transparente pentru utilizator, ele fiind doar zone de memorare temporara a datelor. Pentru lucrul cu mai multe fisiere care se gasesc in acelasi director exista functiile: findfirst – (se apeleaza o singura data) pozitioneaza pointerul de fisiere pe primul fisier care indeplineste anumite conditii legate de nume generic ( *.exe, *.*) 20 8. Listingul programului #include <stdio.h> #include <conio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> //elementele listei--------------------------------------------------------typedef struct angajat_ { char nume_angajat[20]; int salariu; long telefon; long nr_legitimatiei; int an_nastere; } angajat; //structura ce contine lista------------------------------------------------typedef struct List_ { angajat data; struct List_ *Next, *Back; } List; List *First=NULL, *Last=NULL; const char *file="angajat.txt"; int iConstr; int operatie; FILE *f; int alocare(angajat *item); angajat inc(angajat Constr); int adaugare_angajat(angajat Constr, int i); int creare(void); int afisare(void); int adaugare(void); int salvare(List *pFirst); int open(void); int sortare(void); int cautare(List *intrep); int modificare(void); int sterge(void); void meniu(void); void contin(List *Constr); //functiile-----------------------------------------------------------------int main() { textcolor(GREEN); while(1) { meniu(); fflush(stdin); switch(operatie) { case 1: clrscr(); puts("\n\n\tBAZA DE DATE A ANGAJATILOR UNEI INTREPRINDERI\n\n"); creare(); break; case 2: clrscr(); afisare(); break; case 3: open(); break; case 4: clrscr(); adaugare();clrscr(); afisare(); break; case 5: clrscr(); sortare(); clrscr(); afisare(); break; 21 case 6: clrscr(); cautare(First); break; case 7: clrscr(); modificare(); clrscr(); afisare(); break; case 8: clrscr(); sterge(); break; case 9: salvare(First); break; case 0: int k; clrscr(); printf("\n\tSalvati modificarile inainte de iesire?\n"); default: printf("\n\t1- Da 2- Nu\n"); scanf("%d",&k); if(k==1) { salvare(First); exit(0); } if(k==2) exit(0); printf("\n\n\tEroare!!!" "\n\t\tTastati o tasta pentru reintoarcere..."); getch(); break; } } return 0; } //alocare de memorie--------------------------------------------------------int alocare(angajat *item) { List *New_item; New_item = (List *)malloc(sizeof(List)); if(!New_item) { printf("\n\tEroare!!!" "\n\tMai incercati o data!!!"); getch(); return 0; } New_item->data = *item; if (First) First->Back = New_item; (*New_item).Back = NULL; (*New_item).Next = First; First = New_item; return 1; } //introducerea datelor despre angajat---------------------------------------angajat inc(angajat Constr) { fflush(stdin); printf("\n\tNumele angajatului: "); scanf("%15s", &Constr.nume_angajat); fflush(stdin); printf("\tSalariul %s: ", Constr.nume_angajat); scanf("%d", &Constr.salariu); fflush(stdin); printf("\tTelefonul %s: ",Constr.nume_angajat); scanf("%ld", &Constr.telefon); fflush(stdin); printf("\tNr_legitimatie %s: ",Constr.nume_angajat); scanf("%ld", &Constr.nr_legitimatiei); fflush(stdin); printf("\tAnul_nasterii %s: ",Constr.nume_angajat); scanf("%d", &Constr.an_nastere); fflush(stdin); 22 return Constr; } //adaugare angajati-------------------------------------------------------int adaugare_angajat(angajat Constr, int i) { int v; do { i++; alocare(&(inc(Constr))); puts("\n\tMai aveti nevoie de audaugat angajati?" " 1 - Da, 0 - Iesire\n"); scanf("%d",&v); } while (v); printf("\n\tLista cu angajati este creata si completatata!!!\n\tTastati o tasta pentru continuare...\n"); getch(); } //crearea listei de angajati-----------------------------------------------int creare() { angajat Constr; int i=0,k; if (First != NULL) { printf("\n\tExista deja o lista,daca continuati " "o pierdeti!!! \n\t1 - Continuare 0-Anulare\n"); scanf("%d",&k); if (k!=1)return 0; else First=NULL; } else { alocare(&(inc(Constr))); } adaugare_angajat(Constr, i); return i; } //--------------------------------------------------------------------------int adaugare() { angajat Constr; int i=0; adaugare_angajat(Constr, i); return i; } //salvarea listei de angajati-----------------------------------------------int salvare(List *First) { angajat Constr; List *intrep; int i=1; int r; intrep=First; if (f=fopen(file, "rb")) { printf("\n\tExista deja fisier," "veti pierde informatia. \n"); printf("\n\n\tPentru rvenire tastati-1 \n\tPentru continuare tastati-0\n"); scanf("%d",&r); if (r) return 0; } f=fopen(file, "wb"); while(intrep) { fwrite(&intrep->data, sizeof(angajat),1,f); intrep= intrep->Next; ++i; } fclose(f); printf("\n\tLista cu angajati este salvata cu succes" "in fisier <%s>!!!", file); getch(); return 0; 23 } //deschiderea fisierului pt actiune-----------------------------------------int open() { angajat constr; int i=0; int c; if (First != NULL) { printf("\n\tLista exista, daca continuati " "o pierdeti!!! \n\t1 - Continuare 0-Anulare\n"); scanf("%d",&c); if (!c) return 0; else First=NULL; } if(!(f=fopen(file, "rb"))) { puts("\n\t\tCreati mai intii fisierul!!!"); getch(); return 0; } fread(&constr,sizeof(angajat), 1, f); while(! feof(f)) { alocare(&constr); fread(&constr, sizeof(angajat), 1, f); } return (afisare()); } //continutul listei de angajati---------------------------------------------void contin(List *intrep) { int i=1; while(intrep) { printf(" | %2d |", i); printf(" %16s|",intrep->data.nume_angajat); printf(" %8d|",intrep->data.salariu); printf(" %8ld|",intrep->data.telefon); printf(" %13ld |",intrep->data.nr_legitimatiei); printf(" %6d|",intrep->data.an_nastere); printf(" _____________________________________________________________________________ __\n"); intrep = intrep->Next; ++i; } } //afisare listei de angajati------------------------------------------------int afisare() { int j=1; puts("\n\t\t\tINFORMATIA DESPRE Angajati:\n"); printf(" _____________________________________________________________________________ \n"); printf(" | Nr. | Nume angajat | salariu | telefon | nr_legitimatiei |an_naster|\n"); printf(" |____________________________________________________________________________ _|\n"); contin(First); printf("\n\t\tTastati o tasta pentru continuare..."); getch(); return (j-1); } //sortare listei de angajati------------------------------------------------int sortare() { List *temp1, *temp2, *min, *prev, *Sort=NULL; int i, j, iConstr=0,ales; 24 iConstr=afisare()-1; puts(""); printf("\n\t Sortati lista dupa:\n"); puts("\t 1 - Numele angajatului"); puts("\t 2 - Nr_legitimatiei"); puts("\t 3 - Anul nasterii"); puts("\t 0 - Inapoi\n"); scanf("%d",&ales); switch(ales) { case 1: { while(First != NULL) { prev = NULL; min = temp1 = First; temp2 = First -> Next; while ( temp2 != NULL ) { if(strcmpi(min -> data.nume_angajat, temp2 -> data.nume_angajat) > 0) { min = temp2; prev = temp1; } temp1 = temp2; temp2 = temp2 -> Next; } if(prev == NULL) First = min -> Next; else prev -> Next = min -> Next; min -> Next = NULL; if( Sort == NULL ) Sort = min; else { temp1 = Sort; while( temp1 -> Next != NULL) temp1 = temp1 -> Next; temp1 -> Next = min; } } break; } case 2: { while(First != NULL) { prev = NULL; min = temp1 = First; temp2 = First -> Next; while ( temp2 != NULL ) { if(min -> data.nr_legitimatiei > temp2 -> data.nr_legitimatiei) { min = temp2; prev = temp1; } temp1 = temp2; temp2 = temp2 -> Next; } if(prev == NULL) First = min -> Next; else prev -> Next = min -> Next; min -> Next = NULL; if( Sort == NULL ) Sort = min; else { temp1 = Sort; while( temp1 -> Next != NULL) temp1 = temp1 -> Next; temp1 -> Next = min; } } break; 25 } case 3: { while(First != NULL) { prev = NULL; min = temp1 = First; temp2 = First -> Next; while ( temp2 != NULL ) { if(min -> data.an_nastere > temp2 -> data.an_nastere) { min = temp2; prev = temp1; } temp1 = temp2; temp2 = temp2 -> Next; } if(prev == NULL) First = min -> Next; else prev -> Next = min -> Next; min -> Next = NULL; if( Sort == NULL ) Sort = min; else { temp1 = Sort; while( temp1 -> Next != NULL) temp1 = temp1 -> Next; temp1 -> Next = min; } break; } } case 0: break; default: { printf("\n\t\tNu ati ales varianta potrivita!!! \n\t"); getch(); break; exit(0); } } First=Sort; printf("\n\t\tTastati o tasta pentru continuare... "); getch(); return 0; } //cautarea in lista de angajati---------------------------------------------int cautare(List *intrep) { int j=1; char eticheta[20]; List *Start; Start=intrep; if (First == NULL) { printf("\n\t\tLista este vida!!!"); getch(); return 0; } printf("\n\t\tIntroduceti numele angajatului cautat:\n "); gets(eticheta); puts("\n\t\t\tINFORMATIA DESPRE ANGAJATI:\n"); printf(" __________________________________________________________________________ \n"); printf(" | Nr. | Nume angajat | salariu | telefon | nr_legitimatiei |an_nas|\n"); printf(" |__________________________________________________________________________|\ n"); while(Start) { if(strcmpi(Start->data.nume_angajat, eticheta) == 0) { printf(" | %2d |", j); 26 printf(" %-15s|",Start->data.nume_angajat); printf(" %-8d |",Start->data.salariu); printf(" %-8ld |",Start->data.telefon); printf(" %13ld |",Start->data.nr_legitimatiei); printf(" %6d |\n",Start->data.an_nastere); printf(" __________________________________________________________________________\n" ); ++j; } Start = Start->Next; } printf("\n\t\tTastati o tasta pentru continuare..."); getch(); return (j-1); } //modificarea listei de angajati--------------------------------------------int modificare() { int i,k,ales; List *Curent; angajat Constr; Curent = First; afisare(); printf("\n\tIntroduceti numarul elementului pe care doriti sa-l monificati: "); scanf("%d", &ales); fflush(stdin); if (ales < 0) return 0; for (i=1; i<ales; ++i) { Curent = Curent->Next; if (!Curent) { printf("\n\t\tEroare!!! Elementul nu este gasit!Tastati o tasta pentru continuare..."); getch(); return 0; } } printf("\n\tCare cimp doriti sa modificati?" "\n\t1- Numele angajatului" "\n\t2- Salariul" "\n\t3- Telefonul" "\n\t4- Nr_legitimatiei" "\n\t5- Anul nasterii\n"); scanf("%d",&k); switch(k) { case 1: printf("\n\tIntroduceti date noi despre "); printf("\n\tnumele angajatului: "); scanf("%s",&Curent->data.nume_angajat); fflush(stdin); break; case 2: printf("\n\tIntroduceti date noi despre "); printf("\n\tsalariuarii: "); scanf("%d",&Curent->data.salariu); fflush(stdin); break; case 3: printf("\n\tIntroduceti date noi despre "); 27 printf("\n\ttelefonarii: "); scanf("%ld", &Curent->data.telefon); fflush(stdin); break; case 4: printf("\n\tIntroduceti date noi despre "); printf("\n\tnr. politei de asigurare: "); scanf("%ld", &Curent->data.nr_legitimatiei); fflush(stdin); break; case 5: printf("\n\tIntroduceti date noi despre "); printf("\n\tanul nasterii: "); scanf("%d", &Curent->data.an_nastere); fflush(stdin); break; default: printf("\n\tNu ati ales nici o varianta potrivita!!! \n\t"); getch(); return 0; } return 1; } //stergere-------------------------------------------------------------------------int sterge() { int k; List *del; if (!First) { printf("\n\t\tNu exista nici o lista de sters!!!"); getch(); return 0; } printf("\n\tAceasta operatie va sterge lista de angajati\n\t" "din memorie!!!" "\n\tDoriti sa stergeti lista???" "\n\t1 - Da, 0- Nu\n", file); scanf("%d",&k); if (k) { while(First) { del = First; First = First->Next; free(del); } printf("\n\tLista este stearsa cu succes!!!"); getch(); } return 1; } //meniul--------------------------------------------------------------------void meniu() { clrscr(); puts("\n\n\n\n\n\n\n"); puts("\t====================MENIU======================="); puts("\t|| ||"); puts("\t|| ||"); puts("\t|| 1:--==CREARE==-||"); puts("\t|| 2:--==AFISARE==-||"); puts("\t|| 3:--==DESCHIDERE==-||"); puts("\t|| 4:--==ADAUGARE==-||"); 28 puts("\t|| 5:--==SORTARE==-||"); puts("\t|| 6:--==CAUTARE==-||"); puts("\t|| 7:--==MODIFICARE==||"); puts("\t|| 8:--==ELIMINARE==-||"); puts("\t|| 9:--==SALVARE==-||"); puts("\t|| 0:--==IESIRE==-||"); puts("\t|| ||"); puts("\t|| ||"); puts("\t================================================"); puts("\n\tINTRODUCETI COMANDA:\n"); scanf("%d",&operatie); } 29 9. Rezultatul final: 30 31 10. Concluzii: Lucrarea data reprezinta un model de prelucrare a informatiei unei baze de date ce contine date despre angajatii unei intreprinderi. Informatia despre angajati se introduce cu ajutorul unei liste, lista la rindul sau este salvata in sructura si pastrata in fisier, informatia este prelucrata prin intermediul tablourelor unidimensionale. Informatia se pastreaza in fisierul: „angajati.txt” si este manipulata cu ajutorul meniului. 32 11. Bibliografie 1. Colectia Informanica nr. 8 – Culegere de probleme – Varianta Pascal Editura Else. D. Botofei; R. Tamplaru; L. Schiopu. 2. L.Negrescu.Iniţiere în limbajul C,C++.-Cluj,1999. 3. Internet: www.referat.ro, www.pcmagazine.ro, www.elth.srv.ro, www.proeducation.md/infpro. 4. Limbajul Pascal structuri dinamice de date, grafuri si programare orientata pe obiecte. Editura Else . E. Popescu; S. Vitelaru; M. Codres. 5. Sergiu G.Istrati.Programarea în limbajele C,C++.UTM Chişinău 2004. 6. G.D.Mateescu.C++limbaj de programare.-Bucureşti:ed.Petrion 1998. 7.D.M.Popovici,I.M.Popovici,I.Tănase.C++.Tehnologia orientată pe obiecte.Aplicaţii.-Bucureşti:ed.Teora,1996 33