p2: trinaesta nedelja datoteke primena ulančanih lista - datoteke... · ©programiranje 2 – 2009...

34
©Programiranje 2 – 2009 ETF Beograd 1/20 P2: Trinaesta nedelja Datoteke Primena ulančanih lista

Upload: vungoc

Post on 29-Nov-2018

224 views

Category:

Documents


1 download

TRANSCRIPT

©Programiranje 2 – 2009 ETF Beograd 1/20

P2: Trinaesta nedelja Datoteke

Primena ulančanih lista

©Programiranje 2 – 2009 ETF Beograd 2/20

Zadatak 1

• Sastaviti program na programskom jeziku C za spajanje sadržaja nekoliko sekvencijalnih tekst datoteka u jednu izlaznu datoteku. Imena datoteka se zadaju kao parametri u komandnoj liniji. Ime izlazne datoteke je prvi parametar. Ukoliko je prvi parametar "-", izlazna datoteka je stdout.

©Programiranje 2 – 2009 ETF Beograd 3/20

Rad sa datotekama (1)

• Ulaz i izlaz podataka omogućava komunikaciju programa sa spoljnim svetom – unos podataka, njihova obrada i odlaganje (ili prikaz) rezultata odgovarajuće

obrade su osnova svakog ozbiljnijeg programa

• Za skladištenje velikih količina podataka koriste se datoteke

• Prema načinu skladištenja podataka postoje tekstualne i binarne datoteke – tekst datoteke se sastoje od niza čitljivih znakova koji su logički podeljeni u

redove, a za reprezentaciju znakova u memoriji koristi se ASCII tabela

– binarne datoteke se sastoje od niza bajtova čiji sadržaj nije razumljiv oku čitaoca

• Datoteke na jeziku C su sekvencijalnog tipa – pristup je moguć samo prema redosledu smeštanja u datoteku

– samo kod binarnih datoteka je moguće podesiti mesto (bajt) u datoteci od koga će početi sekvencijalni pristup

• Prilikom rada sa datotekama postoje sledeće radnje: otvaranje datoteke, pristup datoteci, ispitivanje stanja datoteke i zatvaranje datoteke

©Programiranje 2 – 2009 ETF Beograd 4/20

Rad sa datotekama (2)

• Sve funkcije za rad sa datotekama na jeziku C se nalaze u standardnom zaglavlju <stdio.h>

• Svaka datoteka koju želimo da koristimo u programu mora biti logički povezana sa odgovarajućim pokazivačem na strukturu FILE

FILE *dat;

• Otvaranje datoteke:

FILE *fopen (char *filename, char *arg);

– filename predstavlja fizičko ime datoteke na disku

– arg je string kojim se navodi način za pristup datoteci

– osnovni režim - "r" - read, "w" - write, "a" – append

– dodavanje + omogućava naizmenično čitanje i pisanje, bez obzira na osnovni režim rada – "r+", "w+", "a+"

– za rad sa binarnim datotekama, osnovnim argumentima treba dodati još slovo b – "rb", "wb+" i sl.

– funkcija fopen() vraća pokazivač na logičku strukturu tipa FILE ukoliko je poziv uspešan ili NULL ako je poziv neuspešan (npr. nepostojeća datoteka)

©Programiranje 2 – 2009 ETF Beograd 5/20

Rad sa datotekama (3)

• Zatvaranje datoteke:

int fclose (FILE *dat);

• Čitanje i pisanje bez konverzije: int fgetc (FILE *dat);

int getc (FILE *dat);

int fputc (int zn, FILE *dat);

int putc (int zn, FILE *dat); – vrednosti ovih funkcija je ili kod učitanog/ispisanog znaka ili znak EOF ukoliko je

došlo do greške – znak EOF (end-of-file) označava kraj datoteke ili grešku

• Čitanje i pisanje sa konverzijom: fscanf(FILE *, ...);

fprintf(FILE *, ...);

– ponašanje i argumenti isti kao kod regularnih scanf/printf funkcija

• Provera kraja datoteke int feof (FILE *dat);

• Na jeziku C postoje tri predefinisane datoteke koje se automatski otvaraju: stdin (glavni ulaz), stdout (glavni izlaz) i stderr (izlaz za greške)

©Programiranje 2 – 2009 ETF Beograd 6/20

Zadatak 1 - rešenje

#include <stdio.h>

#include <string.h>

void prepis (FILE *, FILE*); // prototip funkcije

void main (int argc, char *argv[]) {

FILE *ulaz, *izlaz;

char *prg= argv[0];

izlaz = stdout;

if (argc > 1) {

argc--;

if (strcmp(*++argv, "-") != 0)

if ((izlaz = fopen(*argv, "w")) == NULL) {

fprintf(stderr,

"%s:greska u otvaranju dat %s", s\n",prg,*argv);

exit(1);

}

}

©Programiranje 2 – 2009 ETF Beograd 7/20

Zadatak 1

while(--argc > 0) // prolazi se kroz niz argumenata

if ((ulaz = fopen(*++argv, "r")) != NULL)

prepis(ulaz, izlaz), fclose(ulaz);

else {

fprintf(stderr,"%s: ne postoji datoteka %s\n", progr, *argv);

exit(2);

}

fclose(izlaz);

exit (0);

}

// definicija funckcije

void prepis(FILE *ulaz, FILE *izlaz) {

int c;

while((c=getc(ulaz)) != EOF) putc(c, izlaz);

}

©Programiranje 2 – 2009 ETF Beograd 8/20

Zadatak 2 – ispravljanje slova (8.7)

• Napisati program na programskom jeziku C koji ispravlja greške zamenjenih vELIKIH i Malih sLoVa.

• Početak rečenice je prvo slovo od početka fajla, ili prvo slovo posle kraja prethodne rečenice.

• Kraj rečenice označen je nekim interpunkcijskim znakom (".","?","!").

• Između rečenica može da bude jedan ili više blanko znakova.

• Tekst se čita iz fajla "recenice.pod", a rezultat obrade se ispisuje u fajl "recenice.rez".

©Programiranje 2 – 2009 ETF Beograd 10/20

Zadatak 2 - rešenje

#include <stdio.h>

#include <ctype.h>

#include <string.h>

void recenice (const char ulaz[], const char izlaz[]) {

int zn, prvi=1;

FILE *ul = fopen (ulaz, "r"), *izl = fopen (izlaz, "w");

if(!ul || !izl) {

fprintf(stderr, "Greska u otvaranju datoteka\n"); return;

}

while ((zn = fgetc (ul)) != EOF) {

if (isupper(zn) && isalpha(zn)) {

if (!prvi) zn += 'a' - 'A';

else prvi = 0; // Početno VELIKO; naredno nije početno

}

else if (islower(zn) && isalpha(zn))

if (prvi) { zn += 'A' - 'a'; prvi = 0; }

else if (zn=='.' || zn=='!' || zn=='?') prvi = 1;

fputc (zn, izl); //Stampanje prilagodjenog znaka

}

fclose (ul); fclose (izl);

}

©Programiranje 2 – 2009 ETF Beograd 11/20

Zadatak 2

void main () {

char ulaz[30], izlaz[30];

while (1) {

printf ("Ime ulazne datoteke? ");

scanf ("%s", ulaz);

if (strcmp (ulaz, "***") == 0) break;

printf ("Ime izlazne datoteke? ");

scanf ("%s", izlaz);

if (strcmp (izlaz, "***") == 0) break;

recenice (ulaz, izlaz);

}

}

©Programiranje 2 – 2009 ETF Beograd 12/20

Zadatak 3

• Napisati program na programskom jeziku C koji računa vrednost verižnog razlomka. Program čita podatke iz datoteke "verizni.pod".

• U prvom redu ove datoteke zapisan je ceo broj N, i potom još N realnih brojeva: a[0], a[1],..., a[N-1]. U narednom redu su zapisana tri realna broja koji su redom donja granica, gornja granica i korak promenlljive x.

• Izlaz se zapisuje u datoteku "verizni.rez". Na početku ove tekstualne datoteke treba zapisati učitane podatke, a potom tabelirati verižnu funkciju.

©Programiranje 2 – 2009 ETF Beograd 13/20

Zadatak 3 - rešenje

/* verizni.c - Izracunavanje vrednosti veriznog

razlomka */

#include <stdio.h>

#define N 30

double v (double a[], int n, double x) {

int i; double u = 0;

for (i=n-1; i>=0; u=a[i--]/(x+u));

return u;

}

©Programiranje 2 – 2009 ETF Beograd 14/20

Zadatak 3

void main () {

double a[N], x, xmin, xmax, dx;

int n, i, br;

FILE *ul, *izl;

ul = fopen ("verizni.pod", "r"); izl = fopen ("verizni.rez", "w");

br = fscanf (ul, "%d", &n);

if(br<1) { fprintf(stderr, "Neodgovarajuci br. koef."); goto END; }

fprintf (izl, "n=%d\n", n);

fprintf (izl, "A=");

for (i=0; i<n; i++) {

br = fscanf (ul, "%lf", &a[i]);

if(br<1) {fprintf(stderr, "Neodgovarajuci koef."); goto END;}

fprintf (izl, " %.2f", a[i]);

}

fputc ('\n', izl);

br = fscanf (ul, "%lf%lf%lf", &xmin, &xmax, &dx);

if(br<3) {fprintf(stderr, "Neodgovarajuci opseg za x"); goto END;}

fprintf (izl, "xmin, xmax, dx= %.2f, %.2f, %.2f\n", xmin, xmax, dx);

fprintf (izl, "\n x v(x)\n");

for (x=xmin; x<=xmax; x+=dx)

fprintf (izl, "%10.2f%12.2f\n", x, v(a,n,x));

END: fclose (ul); fclose (izl);

}

©Programiranje 2 – 2009 ETF Beograd 15/20

Zadatak 4 – binarne datoteke (C120)

• Data je binarna datoteka vlasnici.dat koja je formirana na osnovu informacija o vlasnicima kućnih ljubimaca. Svaki zapis u datoteci sadrži ime i prezime vlasnika (maksimalno 30 karaktera), ime kućnog ljubimca (maksimalno 20 karaktera), informaciju da li je ljubimac vakcinisan (jedan karakter) i identifikacioni zapis. Identifikacioni zapis sadrži šifru ljubimca (ceo broj) i jedan karakter koji označava vrstu ljubimca ('P' za pse, 'M' za mačke i 'O' za ostale vrste).

• Napisati program na programskom jeziku C koji na osnovu sadržaja navedene datoteke formira listu samo onih zapisa koji se odnose na mačke.

• Zatim je potrebno sa standardnog ulaza čitati šifre mačaka sve dok se ne unese negativna vrednost. Za svaku učitanu šifru mačke, potrebno je proveriti da li je mačka vakcinisana, i u slučaju da nije, ispisati informacije o njenom vlasniku na standardnom izlazu.

• Podrazumeva se da datoteka vlasnici.dat već postoji.

©Programiranje 2 – 2009 ETF Beograd 16/20

Rad sa binarnim datotekama

• Binarna datoteka se mora otvoriti na odgovarajući način ("rb", "wb", "rb+"…)

• Čitanje i pisanje podataka size_t fread(void *adr, size_t size, size_t count, FILE *dat);

size_t fwrite(void *adr, size_t size, size_t count, FILE *dat);

(stddef.h: typedef unsigned int size_t;)

– adr je adresa od koje se vrši smeštanje pročitanih/ispisivanje podataka

– size je veličina pojedinačnog podatka, count broj podataka

– dat je datoteka iz koje se čita ili u koju se piše

• Pozicioniranje u datoteci u odnosu na zadati reper

int fseek(FILE *dat,long pomeraj, int reper);

– reperne tačke SEEK_SET (početak datoteke), SEEK_CUR (trenutna pozicija), SEEK_END (kraj datoteke)

• Određivanje trenutne pozicije u datoteci long ftell (FILE *dat);

• Povratak na početak datoteke void rewind (FILE *dat);

©Programiranje 2 – 2009 ETF Beograd 17/20

Zadatak 4 - rešenje

#include <stdio.h>

#include <stdlib.h>

typedef struct id {

int broj;

char vrsta;

} identifikacioni_zapis;

typedef struct zapis {

char ime_vlasnika[31];

char ime_ljubimca[21];

char vakcina;

identifikacioni_zapis id_zapis;

} Zapis;

typedef struct element_liste {

Zapis z;

element_liste *sled;

} Element_liste;

typedef struct lista {

Element_liste *prvi;

Element_liste *poslednji;

} Lista;

Svaki zapis u datoteci sadrži ime i prezime

vlasnika (maksimalno 30 k.),

ime kućnog ljubimca (maksimalno 20 k.),

informaciju da li je ljubimac vakcinisan (1 k.) i

identifikacioni zapis.

Identifikacioni zapis sadrži šifru ljubimca

(ceo broj) i jedan karakter koji označava vrstu

('P' za pse, 'M' za mačke i 'O' za ostale vrste).

5 3 15 -2

prvi poslednji

©Programiranje 2 – 2009 ETF Beograd 18/20

Zadatak 4

void main() {

FILE *f = fopen("vlasnici.dat", "rb");

Zapis z;

Lista* l;

if( !f ) {

printf("Neuspelo otvaranje datoteke 'vlasnici.dat'\n");

return;

}

l = napraviListu();

if( !l ) {

printf("Neuspelo stvaranje liste\n");

return;

}

while( fread(&z, sizeof(Zapis), 1, f) == 1 )

if( z.id_zapis.vrsta == 'M' )

dodajUListu(l, &z);

fclose(f);

pronadjiNevakcinisane(l, 'M');

obrisiListu(l);

l = NULL;

}

©Programiranje 2 – 2009 ETF Beograd 19/20

Zadatak 4

Lista *napraviListu(){

Lista *l = malloc( sizeof(Lista) );

if( l )

l->prvi = l->poslednji = NULL;

return l;

}

void obrisiListu(Lista *l) {

if( ! l ) return;

while( l->prvi ) {

Element_liste *tek = l->prvi;

l->prvi = l->prvi->sled;

free(tek);

}

free(l);

}

©Programiranje 2 – 2009 ETF Beograd 20/20

Zadatak 4

void dodajUListu(Lista *l, Zapis *zap) {

Element_liste *novi;

if( ! l || ! zap ) return;

novi = malloc( sizeof(Element_liste) );

if( ! novi ) {

printf("Neuspela alokacija memorije!\n");

exit(0);

}

strcpy(novi->z.ime_vlasnika, zap->ime_vlasnika);

strcpy(novi->z.ime_ljubimca, zap->ime_ljubimca);

novi->z.vakcina = zap->vakcina;

novi->z.id_zapis = zap->id_zapis;

novi->sled = NULL;

if( ! l->prvi )

l->prvi = novi;

else

l->poslednji->sled = novi;

l->poslednji = novi;

}

©Programiranje 2 – 2009 ETF Beograd 21/20

Zadatak 4

void pronadjiNevakcinisane(Lista *l, char tip) {

if( ! l ) return;

// Lista prazna???

printf("Vlasnici ciji ljubimci nisu

vakcinisani:\n");

Element_liste *tek = l->prvi;

while(tek){

if(tek->z.id_zapis.vrsta == tip &&

tek->z.vakcina == 0)

printf("%s\n", tek->z.ime_vlasnika);

tek = tek->sled;

}

}

I1

• Tekstualna datoteka kalendar.txt u svakom redu sadrži informacije o zakazanim aktivnostima u jednom danu. Za svaku aktivnost navodi se vreme početka, vreme završetka i kratak opis. Napisati program na programskom jeziku C koji čita red po red iz datoteke, pravi strukturu aktivnosti na osnovu podataka iz pročitanog reda i stavlja je u listu u hronološkom poretku. Potom, polazeći od početka liste pronalazi aktivnosti koje se preklapaju. Ako se detektuje preklapanje između dve aktivnosti, iz liste se izbacuje aktivnost koja počinje kasnije ili, ako obe počinju u isto vreme, izbacuje se ona koja je druga u poretku. Aktivnost se može preklapati sa proizvoljno mnogo drugih aktivnosti. Aktivnosti koje se izbace iz liste, ispisuju se u izlaznu tekstualnu datoteku preklapanja.txt. Za kratak opis aktivnosti koristi se maksimalno 80 znakova. Na kraju program treba da ispravno oslobodi zauzete resurse.

©Programiranje 2 – 2009 ETF Beograd 22/20

I1

• Zadatak ilustruje – Upotrebu listi

– Rad sa datotekama, čitanje sa konverzijom

– Slojevito programiranje

– Pisanje funkcija „unapred“

©Programiranje 2 – 2009 ETF Beograd 23/20

I1

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define DEFAULT_TEXT_LEN 80

typedef struct sTime {

int hours, minutes;

} Time;

typedef struct sActivity {

Time startTime, endTime;

char description[DEFAULT_TEXT_LEN + 1];

} Activity;

typedef struct sListElement {

Activity * pActivity;

struct sListElement * next;

} ListElement;

©Programiranje 2 – 2009 ETF Beograd 24/20

int main() {

ListElement * activityList = NULL; Activity * currActivity;

int s = 0;

FILE* scheduleFile = fopen("kalendar.txt", "r");

FILE* overlapingFile = fopen("preklapanja.txt", "w");

FILE* debugFile = fopen("debug.txt", "w");

if (scheduleFile == NULL) {

fprintf(stderr, "Cannot open input file."); s = 1; goto END;

}

/* Ista provera za overlappingFile i debugFile */

while ((currActivity = readActivity(scheduleFile)) != NULL) {

activityList = insert(activityList, currActivity);

}

print(activityList, debugFile);

removeOverlappedActivities(activityList, overlapingFile);

print(activityList, debugFile);

END: if (scheduleFile) fclose(scheduleFile);

if (overlapingFile) fclose(overlapingFile);

if (debugFile) fclose(debugFile);

// dealocate the list...

return s;

} ©Programiranje 2 – 2009 ETF Beograd 25/20

Activity* readActivity(FILE* f) {

int numOfData;

Activity* actv =

(Activity*)malloc(sizeof(Activity));

if(!actv) return NULL; //allocation error

numOfData = fscanf(f, "%d:%d-%d:%d ",

&actv->startTime.hours, &actv->startTime.minutes,

&actv->endTime.hours, &actv->endTime.minutes);

if (numOfData == EOF || numOfData < 4) {

free(actv); //nepotrebno smo alocirali

return NULL;

}

fgets(actv->description, DEFAULT_TEXT_LEN, f);

return actv;

} ©Programiranje 2 – 2009 ETF Beograd 26/20

fgets(char* buff, int len, FILE* f)

Učitava u buff najviše len karaktera, ili do pojave prvog \n znaka,

čitajući iz fajla f. Znak \n ostaje u stringu, iza njega dodaje \0!

I1

ListElement* insert(ListElement* lstFirst, Activity* actv) {

ListElement* newActv = (ListElement*)

malloc(sizeof(ListElement));

newActv->pActivity = actv;

ListElement* curr = lstFirst, *prev = NULL;

while (curr) {

if (isBeforeOrEqualAct(curr->pActivity, actv))

prev = curr, curr = curr - > next;

else break; // pronasli smo mesto!

}

if (!prev) lstFirst = newActv; //prvi element

else prev->next = newActv; //

newActv->next = curr; // ulancavanmo u postojeću

return lstFirst; // vraćamo glavu liste

} ©Programiranje 2 – 2009 ETF Beograd 27/20

I1

ListElement* removeOverlappedActivities(ListElement* aList, FILE*

output) {

ListElement * pi = aList, * pj = NULL, * pjPrev, * old;

while (pi) {

pj = pi->next;

while (pj) {

if (isOverlaped(pi->pActivity, pj->pActivity)) {

print(pj->pActivity, output);

pi->next = pj->next;

free(pj->pActivity);

free(pj);

pj = pi->next;

} else {

pj = pj->next;

}

}

pi = pi->next;

}

return aList;

} ©Programiranje 2 – 2009 ETF Beograd 28/20

void print(Activity * actv, FILE * f) {

fprintf(f, "%d:%d-%d:%d ",

actv-> startTime.hours, actv-> startTime.minutes,

actv->endTime.hours, actv-> endTime.minutes);

fputs(actv - > description, f);

}

I1

int isOverlaped(Activity * a1, Activity * a2) {

return (isBeforeOrEqual(a1->startTime, a2->startTime) &&

isBefore(a2->startTime, a1->endTime)) ||

(isBeforeOrEqual(a2->startTime, a1->startTime)&&

isBefore(a1->startTime, a2->endTime));

}

int isBeforeOrEqual(Time t1, Time t2) {

return (t1.hours < t2.hours) ||

(t1.hours == t2.hours && t1.minutes <= t2.minutes);

}

int isBefore(Time t1, Time t2) {

return (t1.hours < t2.hours) ||

(t1.hours == t2.hours && t1.minutes < t2.minutes);

} ©Programiranje 2 – 2009 ETF Beograd 29/20

Ako „kasniji“ događaj započinje pre završetka drugog,

tada postoji preklapanje.

I1

int isBeforeOrEqualAct(Activity* a1, Activity* a2) {

return isBeforeOrEqual(

a1->startTime, a2->startTime);

}

©Programiranje 2 – 2009 ETF Beograd 30/20

I1 - Prototipovi funkcija

ListElement* insert(ListElement* l, Activity* act);

Activity* readActivity(FILE* f);

ListElement* removeOverlappedActivities(ListElement*

aList, FILE* output);

void print(Activity * actv, FILE * f);

int isOverlaped(Activity * a1, Activity * a2)

int isBeforeOrEqual(Time t1, Time t2);

int isBefore (Time t1, Time t2);

int isBeforeOrEqualAct(Activity* a1, Activity* a2);

Ostaviti prostora na početku; dodati potpise funkcija kada budu napisane.

Tehnički, ovo bi trebalo da se nalazi ispred main-a!

©Programiranje 2 – 2009 ETF Beograd 31/20

I2

Napraviti program na programskom jeziku C koji obrađuje datoteku koja sadrži izvorni program na programskom jeziku C. Potrebno je datoteku prepisati u novu datoteku, tako da se izostave svi komentari i očuva uređenost teksta po redovima. Imena ulazne i izlazne datoteke se zadaju kao prvi

i drugi parametar komandne linije i imaju najviše 30 znakova. U slučaju da dođe do greške pri otvaranju neke od datoteka, ispisati poruku o grešci i prekinuti izvršavanje programa.

©Programiranje 2 – 2009 ETF Beograd 32/20

I2

©Programiranje 2 – 2009 ETF Beograd 33/20

Znak Značenje?

/ • Deljenje? • Potencijalni početak

komentara ako trenutno nismo u komentaru

• Potencijalni završetak komentara ako trenutno jesmo u komentaru i bio znak *

* • Množenje? • Početak komentara ako

trenutno nismo u komentaru i prethodno je bio znak /

• Potencijalni završetak komentara ako smo već u komentaru

I2

#include <stdio.h>

enum StanjeObrade {

VAN_K, POC_K, UNUTAR_K, KRAJ_K

};

int main(int argc, char * argv[]) {

int znak;

enum StanjeObrade stanje = VAN_K;

FILE * ul, * izl;

if (NULL == (ul = fopen(argv[1], "r"))) {

fprintf(stderr,

"Doslo je do greske pri otvaranju %s\n", argv[1]);

return 1;

}

if (NULL == (izl = fopen(argv[2], "w"))) {

fprintf(stderr,

"Doslo je do greske pri otvaranju %s\n", argv[2]);

return 2;

} // nastavlja se…

©Programiranje 2 – 2009 ETF Beograd 34/20

I2

while ((znak = fgetc(ul)) != EOF) {

switch (stanje) {

case VAN_K:

if ('/' == znak) stanje = POC_K;

else fputc(znak, izl);

break;

case POC_K:

if ('*' == znak) stanje = UNUTAR_K;

else stanje = VAN_K, fputc('/', izl), fputc(znak, izl);

break;

case UNUTAR_K:

if ('*' == znak) stanje = KRAJ_K;

break;

case KRAJ_K:

if ('/' == znak) stanje = VAN_K;

else stanje = UNUTAR_K;

break;

}

}

fclose(ul);

fclose(izl);

return 0;

} ©Programiranje 2 – 2009 ETF Beograd 35/20

VAN_K

POC_K

UNUTAR_K

KRAJ_K

/ *

*

Ne / /

Ne *