C-ohjelmoinnin harjoitustyö
C-ohjelmoinnin harjoitustyö syksyltä 1994. Tämä ohjelma ilmeisesti … tekee jotakin
/* C-Ohjelmointi, syksy 1994 Heikki Siltala Harjoitustyo, tehtava 5 Ohjelman kaytto: NIMI tiedosto1 tiedosto2 sivunpituus Kaikki parametrit on aina annettava !!! Tiedosto1 on tiedosto, jossa olevien sanojen mukaan lista laaditaan. Tiedosto2 on tiedosto, josta katsotaan sanojen esiintymispaikat. Sivunpituus ilmoittaa, montako rivia on sivussa. Jos tama parametri on <=0 kaytetaan oletusarvoa. Halutessasi kayttaa samaa tiedostoa niin sanojen listaanluvussa kuin niiden esiitymisien etsinnassakin, syota sama tiedostonimi kahteen kertaan! */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> /* Joukko makroja, jotka ilmoittavat virhetilanteista ja keskeyttavat ohjelman siististi: */ #define MUISTI_LOPPUI\ do\ {\ printf("Muisti loppui. Ohjelma keskeytyy...\n\a");\ exit(1);\ }\ while(0) #define TIEDOSTOVIRHE\ do\ {\ printf("Tiedostoa ei voida avata. Ohjelma keskeytyy...\n\a");\ exit(1);\ }\ while(0) #define EI_SANOJA\ do\ {\ printf("Tiedostossa ei ole sanoja. Ohjelma keskeytyy...\n\a");\ exit(1);\ }\ while(0) #define PARAMETRIVIRHE\ do\ {\ printf("Ole hyvä, anna oikea määrä parametreja (3 kpl).\n\a");\ printf("Ohjelma keskeytyy...\n");\ exit(1);\ }\ while(0) /* Jos tama lauseke on TRUE, c on 'valimerkki' */ #define SPACE (c=='\n'||c=='\t'||c==' ') /* (!(isprint(c))) ??? */ #define SANANPITUUS 40 /* Sanan maksimipituus */ #define SIVUNPITUUS 24 /* Sivunpituuden oletusarvo */ /* Seuraavalla makrolla varataan muistia: */ #define M(X) calloc(1,sizeof(struct X)) /* Ohjelmassa kaytettavat listarakenteet: Struct solmu on rakenne, jossa on sana (merkkijono), osoitin seuraavan sanaan ja osoitin listaan, jossa on taman sanan ilmenemispaikkojen rivinumerot. Sturct alkio on rakenne, jossa on ilmenemispaikan rivinumero ja osoitin seuraavaan ilmenemispaikkaan. */ struct alkio { int sij; struct alkio *et; }; struct solmu { char teksti[SANANPITUUS+1]; struct solmu *seur; struct alkio *alku; }; /* Seuraava funktio lukee sanan tiedostosta p, tallettaa tuloksen muuttujaan data ja kasvattaa lf-muuttujaa rivinvaihtojen määrällä. Huomaa: muuttuja tila ilmaisee ollaanko sanan sisalla vai ulkopuolella. Lukeminen tapahtuu merkki kerrallaan. Funktio palauttaa arvon 1 jos luettiin sana, arvon 2 jos sana jouduttiin katkaisemaan ja arvon 0 jos sanaa ei voitu lukea. Huomaa, etta jos sana jouduttiin katkaisemaan, jaljelle jaaneet merkit luetaan pois. */ int lue_sana(FILE *p, int *rivit, char *data, const int max) { int tila=0; int lask=0; register int c; memset(data,'\0',((sizeof(char))*(max+1))); if(feof(p)) return 0; while((c=fgetc(p))!=EOF) { switch(tila) { case 0: if(SPACE) { if(c=='\n') (*rivit)++; if(feof(p)) return 0; } else { tila=1; data[lask++]=c; } break; case 1: if(SPACE) { data[lask]='\0'; if(c=='\n') ungetc(c,p); return 1; } else { data[lask++]=c; if(lask==max) { data[lask]='\0'; while((!(SPACE))&&(!(feof(p)))) c=fgetc(p); if(c=='\n') ungetc(c,p); return 2; } } break; } } data[lask]='\0'; return 1; } /* Seuraava funktio luo uuden solmun. Se saa parametrina merkkijonon, joka sijoitetaan solmuun. Solmun osoite palautetaan. */ struct solmu * tee(const char *tieto) { struct solmu *x; x=M(solmu); if(x==NULL) MUISTI_LOPPUI; strcpy(x->teksti,tieto); x->alku=NULL; return x; } /* Seuraava funktio lisaa sanan oikealle paikalleen listaan, jonka alku annetaan muuttujassa lista. Jos listasta loytyy sama sana, ei tehda mitaan. Funktio palauttaa osoittimen listan alkuun. */ struct solmu * sana_listaan(struct solmu *lista, char *data) { int t; struct solmu *a,*b,*pt; if(!lista) /* Jos lista on tyhja... */ return tee(data); pt=lista; while(pt) { t=strcmp(pt->teksti,data); if(t==0) /* Jos sana on jo ennestaan... */ return lista; if(t>0) /* < ??? */ { if(pt==lista) { a=tee(data); a->seur=lista; return a; } a=tee(data); b=lista; while(b->seur!=pt) b=b->seur; b->seur=a; a->seur=pt; return lista; } pt=pt->seur; } pt=lista; /* Jos sanan paikka on listan lopussa... */ while(pt->seur) pt=pt->seur; a=tee(data); pt->seur=a; a->seur=NULL; return lista; } /* Tama funktio laittaa sanan ilmenemispaikan listaan. Ilmenemispaikka = rivinumero, joka saadaan parametrissa rvt. */ void paikka_listaan(const char *data, const int rvt, struct solmu *lista) { struct solmu *os; struct alkio *ak,*uusi; if(lista) /* Jos lista ei ole tyhja... */ { os=lista; /* Etsitaan sana listasta... */ while((os!=NULL)&&(strcmp(data,os->teksti)!=0)) os=os->seur; if(os) /* Jos loytyi lisataan paikka... */ { ak=os->alku; if(!ak) /* ...ekaksi jos ilmenemispaikkalista */ { /* on tyhja ... */ uusi=M(alkio); os->alku=uusi; uusi->sij=rvt; uusi->et=NULL; } else { while(ak->et) /* ...tai muutoin loppuun. */ ak=ak->et; uusi=M(alkio); ak->et=uusi; uusi->sij=rvt; uusi->et=NULL; } } } } /* Tama funktio kay lapi listan (parametrissa aletaan) ja tulostaa siina olevat sanat esiintymispaikkoineen, formaatti on esim: SANA sivu rivi sivu rivi sivu rivi sivu rivi SANA sivu rivi SANA SANA sivu rivi sivu rivi */ void tulosta(const struct solmu *aletaan, const int sivpit) { struct solmu *o; struct alkio *p; int s,r; o=aletaan; while(o) { printf("%s\n",o->teksti); p=o->alku; while(p) { s=(((p->sij)-1)/sivpit)+1; r=(((p->sij)%sivpit)!=0)?((p->sij)%sivpit):sivpit; printf("%d %d\t",s,r); p=p->et; /* Lasketaan mille sivulle ja sen */ } /* riville sana osuu, aika hassut */ printf("\n\n"); /* lausekkeet...? */ o=o->seur; } } int main(int argc, char *argv[]) { /* MUUTTUJAT: fp: tiedosto-osoitin, katk: pitaa lukua kuinka monta sanaa on katkaistu, lkm: pitaa lukua sanoista, rivi: pitaa ylhaalla milla rivilla ollaan menossa, sivu: sisaltaa sivunpituuden arvon, tilanne: ottaa vastaan lue_sana-funktion palauttaman arvon myohempaa tarkastelua varten, sana: tahan merkkijonoon luetaan sana, pituudesta on viimeinen merkki varattu '\0' varten, eka: osoittaa koko ajan sanalistan alkuun. */ FILE *fp; int katk,lkm,rivi,sivu,tilanne; char sana[SANANPITUUS+1]; struct solmu *eka=NULL; if(argc<4) PARAMETRIVIRHE; sivu=strtol(argv[3],NULL,10); /* Sivunpituus saa arvon */ if(sivu<=0) /* SIVUNPITUUS jos parametri */ sivu=SIVUNPITUUS; /* <=0. */ fp=fopen(argv[1],"r"); if(fp==NULL) TIEDOSTOVIRHE; katk=lkm=0; rivi=1; tilanne=lue_sana(fp,&rivi,sana,SANANPITUUS); while(tilanne!=0) { if(tilanne==2) /* Luetaan sanat listaan */ katk++; lkm++; eka=sana_listaan(eka,sana); tilanne=lue_sana(fp,&rivi,sana,SANANPITUUS); } if(lkm==0) EI_SANOJA; printf("Vaihe 1, sanojen luku listaan:\n"); printf("Luettiin %d sana(a), %d katkaisiin.\n",lkm,katk); fclose(fp); fp=fopen(argv[2],"r"); if(fp==NULL) TIEDOSTOVIRHE; lkm=katk=0; rivi=1; tilanne=lue_sana(fp,&rivi,sana,SANANPITUUS); while(tilanne!=0) { if(tilanne==2) katk++; lkm++; paikka_listaan(sana,rivi,eka); tilanne=lue_sana(fp,&rivi,sana,SANANPITUUS); } fclose(fp); if(lkm==0) EI_SANOJA; printf("Vaihe 2, sanojen esiintymispaikkojen kartoitus:\n"); printf("Luettiin %d sana(a), %d katkaistiin.\n\n",lkm,katk); printf("Sanojen esiintymat:\n"); tulosta(eka,sivu); /* Tulostetaan sanat ja paikat. */ return 0; } /* Heikki Siltalan harjoitustyon LOPPU */