/*
4. zadatak Ispit jul 2017 v2
Ova verzija radi sa razlicitim brojem polozenih ispita po studentu.
U svakoj liniji tekstualne ulazne datoteke studenti.txt.
nalaze se podaci o studentima i podaci o polozenim ispitima.
Svaka linija u ovoj ulaznoj datoteci se sastoji od vise kolona
koje su medjusobno odvojene znakom tackazarez (;).
U prvoj koloni se nalazi ime i prezime studenta (maksimalan broj karaktera je 50).
U drugoj koloni se nalazi broj indeksa studenta (maksimalan broj karaktera je 9).
Potom slede podaci o polozenim ispitima.
Za svaki ispit cuvaju se podaci o sifri ispita (maksimalan broj karaktera je 6)
i oceni sa tog ispita (ocene su u intervalu od 6 do 10).
Prebaciti podatke iz tekstualne datoteke studenti.txt u jednostruko spregnutu listu.
(20 poena)
Sortirati ovu listu (u opadajucem redosledu prema prosecnoj oceni)
i prikazati njen sadrzaj. (15 poena)
Prikaz jedne linije ulazne datoteke:
Marko Markovic;0001/2016;P1;8;OIKT;9;UIS;6;
*/
// Moramo prvo formirati cvor liste i onda njega ubaciti na kraj liste
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> // zbog atoi()
#define SIZE_IME 51 // za jedan vise zbog '\0' na kraju stringa
#define SIZE_BRINDEKSA 10 // za jedan vise zbog '\0' na kraju stringa
#define SIZE_SIFRAISPITA 7 // za jedan vise zbog '\0' na kraju stringa
#define MAX_BRISPITA 50 // 50 ispita tokom studiranja
#define MAX_SIZETEKST 600 // SifraIspita + ; + ocena = 8 + ; + 2 = 11+1=12 . 12 * 50 = 600
typedef struct student STUDENT;
struct student{
char ImeIPrezime[SIZE_IME];
char BrojIndeksa[SIZE_BRINDEKSA];
char SifraIspita[MAX_BRISPITA][SIZE_SIFRAISPITA];
int ocena[MAX_BRISPITA];
float ProsecnaOcena; // ovo je dodato u listu zbog sortiranja po prosecnoj oceni
int BrojPolozenihIspita; // takodje
};
typedef struct cvor CVOR;
typedef CVOR* PCVOR;
struct cvor{
STUDENT podatak;
PCVOR sledeci;
};
// Prikazuje sadrzaj cvorova liste glava
// Argument glava nije pointer jer ova funkcija ne menja glavu.
// Funkcija zavisi od sadrzaja liste glava.
void prikazi_listu(char *tekst, PCVOR glava)
{
int i;
printf("\n%s\n",tekst);
// Ne mora se pitati if(glava == NULL) vec se odmah moze krenuti sa while(glava).
// Tada prazna lista jednostavno nece imati prikazane elemente.
if(glava == NULL) {
printf(" prazna. \n");
} else {
while(glava) {
printf("\n | %-14s %10s ",
glava->podatak.ImeIPrezime,
glava->podatak.BrojIndeksa );
for( i=0; i < glava->podatak.BrojPolozenihIspita; i++ ){
printf(" %4s %2d ",
glava->podatak.SifraIspita[i],
glava->podatak.ocena[i]);
}
printf(" %5.2f %2d | \n",
glava->podatak.ProsecnaOcena,
glava->podatak.BrojPolozenihIspita );
glava = glava->sledeci;
}
}
printf("\n");
}
// Na kraj liste glava dodaje vec napravljeni novi cvor
// Funkcija ne zavisi od sadrzaja liste glava.
void dodaj_na_kraj (PCVOR *glava, PCVOR *rep, PCVOR novi)
{ // dodaje se na kraj liste, pa
novi->sledeci == NULL; // za svaki slucaj, ako ranije nije napisano
if(*glava == NULL) { // ako je lista prazna
*rep = novi; // i rep i glava su taj novi cvor
*glava = novi;
} else { // ako lista nije prazna
(*rep)->sledeci = novi; // sledeci od starog repa je taj novi
*rep = novi; // novi rep je taj novi
}
}
// Sortira listu glava u opadajucem redosledu po float prosecna_ocena
// Funkcija zavisi od sadrzaja liste glava.
void sortiraj_listu_po_float_prosecna_ocena_opadajuce( PCVOR *glava )
{
PCVOR i, j; // pomocne liste i i j
int mem_int; // memorija za pamcenje integer-a
float mem_float; // memorija za pamcenje float-a
char mem_string[SIZE_IME]; // memorija za pamcenje string-a
int k; // brojac for petlje
for ( i=*glava; i; i=i->sledeci ) // i ide od glave do repa
for ( j=*glava; j; j=j->sledeci ) // j ide od glave do repa
if ( i->podatak.ProsecnaOcena > j->podatak.ProsecnaOcena ) { // KRITERIJUM SORTIRANJA
// medjusobno zamenjujemo mesta svim podacima (sadrzajima cvorova i i j)
strcpy(mem_string, i->podatak.ImeIPrezime); // sa stringovima mora strcpy()
strcpy(i->podatak.ImeIPrezime, j->podatak.ImeIPrezime);
strcpy(j->podatak.ImeIPrezime, mem_string);
strcpy(mem_string, i->podatak.BrojIndeksa); // sa stringovima mora strcpy()
strcpy(i->podatak.BrojIndeksa, j->podatak.BrojIndeksa);
strcpy(j->podatak.BrojIndeksa, mem_string);
for( k=0; k < (*glava)->podatak.BrojPolozenihIspita; k++ ){
strcpy(mem_string, i->podatak.SifraIspita[k]); // sa stringovima mora strcpy()
strcpy(i->podatak.SifraIspita[k], j->podatak.SifraIspita[k]);
strcpy(j->podatak.SifraIspita[k], mem_string);
mem_int = i->podatak.ocena[k];
i->podatak.ocena[k] = j->podatak.ocena[k];
j->podatak.ocena[k] = mem_int;
}
mem_float = i->podatak.ProsecnaOcena;
i->podatak.ProsecnaOcena = j->podatak.ProsecnaOcena;
j->podatak.ProsecnaOcena = mem_float;
mem_int = i->podatak.BrojPolozenihIspita;
i->podatak.BrojPolozenihIspita = j->podatak.BrojPolozenihIspita;
j->podatak.BrojPolozenihIspita = mem_int;
}
}
/*
Sortiranje liste u opadajuci redosled nezavisno od sadrzaja cvorova:
Da bismo sortirali listu p, prvo prolazimo kroz nju kako bismo pronasli
element sa najvecom vrednoscu podatka.
Nakon toga uklanjamo taj element iz liste p
i dodajemo ga na pocetak druge liste nova_glava, koja je inicijalno bila prazna.
Ovaj proces ponavljamo sve dok pocetna lista p ne postane prazna.
Na kraju p postaje pokazivac na listu nova_glava u koju su prebaceni svi elementi.
*/
// Sortira listu u opadajucem redosledu nezavisno od sadrzaja cvorova
// Funkcija ne zavisi od sadrzaja liste glava, osim u bloku KRITERIJUM SORTIRANJA !!!
void SortirajUOpadajuciRedosled( PCVOR *glava )
{
PCVOR tekuci, tek_sledeci, max, prethodni, nova_glava;
nova_glava = NULL;
while( *glava ){ // do kraja liste
prethodni = NULL;
max = *glava; // pretpostavljamo da je najveci bas prvi element (glava)
tekuci = *glava;
tek_sledeci = (*glava)->sledeci;
while ( tek_sledeci ){ // KRITERIJUM SORTIRANJA // novi->podatak.ProsecnaOcena
if( max->podatak.ProsecnaOcena < tek_sledeci->podatak.ProsecnaOcena ){
max = tek_sledeci; // max postaje tek_sledeci
prethodni = tekuci;
}
tekuci = tek_sledeci;
tek_sledeci = tek_sledeci->sledeci;
}
if( prethodni == NULL )
*glava = max->sledeci;
else
prethodni->sledeci = max->sledeci;
max->sledeci = NULL;
// dodaj max na kraj nova_glava (bez repa)
if( nova_glava == NULL )
nova_glava = max; // premesta element sa najvecom vrednoscu podatka iz liste p na pocetak liste nova_glava
else {
tekuci = nova_glava;
// prolazi kroz listu nova_glava da bi pronasao njen poslednji element
while( tekuci->sledeci )
tekuci = tekuci->sledeci;
// premesta element sa najvecom vrednoscu podatka iz liste p na kraj liste nova_glava
tekuci->sledeci = max;
}
}
// glava postaje nova_glava
*glava = nova_glava; // Na kraju p postaje pokazivac na listu nova_glava u koju su prebaceni svi elementi.
}
int main(void)
{
FILE *fp;
PCVOR novi=NULL;
PCVOR glava=NULL;
PCVOR rep=NULL;
int i, j, duzina; // j je indeks za tekst2
int ZbirOcenaJednogStudenta;
char tekst[MAX_SIZETEKST];
char tekst2[SIZE_SIFRAISPITA];
char ch;
fp=fopen("studenti.txt","r");
if(fp == NULL){
printf("\n Greska pri otvaranju ulaznog fajla. \n");
exit(1);
}
// Marko Markovic;0001/2016;P1;8;OIKT;9;UIS;6;
// Od pocetka do kraja fajla ucitavamo jedan po jedan podatak i smestamo ga u listu
while( 1 ) { // beskonacna petlja koja se prekida sa break;
novi = (PCVOR)malloc(sizeof(CVOR)); // rezervisemo memoriju za cvor novi
if(novi == NULL) {
printf("\n Greska pri rezervisanju memorije! \n");
exit(1);
}
ZbirOcenaJednogStudenta=0; // resetujemo ZbirOcenaJednogStudenta
// Iz svakog reda ucitavamo redom podatke i smestamo ih u cvor novi
// format za ucitavanje "%[^;]" znaci da ucitavamo dok ne naidjemo na tackazarez !!!
// ZAPAMTI !!!
// Ucitavamo imeiprezime uz proveru da li smo stigli do kraja fajla
if( fscanf(fp,"%[^;]", novi->podatak.ImeIPrezime) == EOF )
break;
//printf("\n|%s|\n",novi->podatak.ImeIPrezime);
// Stao je na tackazarez i sada preskacemo tackazarez
ch = fgetc(fp);
// Ucitavamo broj indeksa uz proveru da li smo stigli do kraja fajla
if( fscanf(fp,"%[^;]", novi->podatak.BrojIndeksa) == EOF )
break;
//printf("\n|%s|\n",novi->podatak.BrojIndeksa);
// Stao je na tackazarez i sada preskacemo tackazarez
ch = fgetc(fp);
// Mi jos ne znamo broj polozenih predmeta (promenljiv je) i moramo da ga odredimo.
// Do sada smo ucitali ImeIPrezime i BrojIndeksa.
// Sada cemo sa fgets() ucitati ostatak reda do kraja reda u tekst i analizirati ga.
fgets(tekst,MAX_SIZETEKST,fp);
duzina = strlen(tekst);
//printf("\n duzina = %d \n",duzina);
// eliminisemo '\n' na kraju tekst jer je fgets() ucitao i '\n'
if( tekst[duzina-1] == '\n' )
tekst[duzina-1]='\0';
//printf("\n tekst = |%s|\n",tekst);
// Sada analiziramo tekst, tj. vadimo parove SifraIspita i ocena, do kraja stringa tekst.
// Na kraju stringa tekst zbog fgets() je '\n' i '\0'.
i=0; // resetujemo indeks stringa tekst[i]
//j=0; // resetujemo indeks stringa novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]
novi->podatak.BrojPolozenihIspita=0; // resetujemo brojac polozenih ispita
while( tekst[i] != '\n' && tekst[i] != '\0' ){
// Stao je na tackazarez i sada preskacemo tackazarez
if( tekst[i] == ';' ){ // preskacemo tackazarez
i++;
// printf("\n Preskocena tackazarez ! \n ");
}
// sada ucitavamo sifru ispita do sledece tackazarez ili do kraja stringa tekst
j=0; // resetujemo indeks stringa novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]
while( tekst[i] != ';' && tekst[i] != '\n' && tekst[i] != '\0' ){
novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita][j++] = tekst[i++];
}
// zavrsavamo string novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]
novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita][j]='\0';
//printf("\n A novi->podatak.SifraIspita = |%s|\n",novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]);
//system("PAUSE");
// eliminisemo '\n' na kraju tekst jer je fgets() ucitao i '\n'
if( novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita][j] == '\n' )
novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita][--j]='\0';
//printf("\n novi->podatak.SifraIspita = |%s|\n",novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]);
// Stao je na tackazarez i sada preskacemo tackazarez
if( tekst[i] == ';' ){ // preskacemo tackazarez
// printf("\n pre tacka zarez, tekst[] = |%s|\n",(tekst + i));
i++;
// printf("\n Preskocena tackazarez ! \n ");
// printf("\n posle tacka zarez, tekst[] = |%s|\n",(tekst + i));
}
// Sada ucitavamo ocena. Prvo u tekst2 ucitamo string, a zatim tekst2 pretvorimo u integer
j=0; // resetujemo indeks stringa tekst2
// sada ucitavamo tekst2 do sledece tackazarez ili do kraja stringa tekst
while( tekst[i] != ';' && tekst[i] != '\n' && tekst[i] != '\0' ){
tekst2[j++] = tekst[i++];
}
tekst2[j]='\0'; // zavrsavamo string tekst2
// eliminisemo '\n' na kraju tekst2 jer je fgets() ucitao i '\n'
if( tekst2[j] == '\n' )
tekst2[--j]='\0';
//printf("\n tekst2 = |%s|\n",tekst2);
// punimo ocena[] koja je integer
novi->podatak.ocena[novi->podatak.BrojPolozenihIspita] = atoi(tekst2);
ZbirOcenaJednogStudenta += novi->podatak.ocena[novi->podatak.BrojPolozenihIspita];
//printf("\n novi->podatak.ocena = %d \n",novi->podatak.ocena[novi->podatak.BrojPolozenihIspita]);
novi->podatak.BrojPolozenihIspita++; // brojimo polozene predmete
//printf("\n novi->podatak.BrojPolozenihIspita = %d \n",novi->podatak.BrojPolozenihIspita );
}
novi->podatak.BrojPolozenihIspita--; // smanjujemo ga jer je poslednji put nepotrebno uvecan
// printf("\n AAAA novi->podatak.BrojPolozenihIspita = %d \n",novi->podatak.BrojPolozenihIspita );
// Sada konacno imamo broj polozenih ispita za tog jednog studenta.
// Racunamo prosecnu ocenu tog jednog studenta:
novi->podatak.ProsecnaOcena = (float)ZbirOcenaJednogStudenta / (float)novi->podatak.BrojPolozenihIspita;
novi->sledeci=NULL; // Obavezno jer je to novi cvor.
dodaj_na_kraj(&glava,&rep,novi);
}
prikazi_listu(" Lista je: ",glava);
//SortirajUOpadajuciRedosled(&glava);
sortiraj_listu_po_float_prosecna_ocena_opadajuce(&glava);
prikazi_listu(" Sortirana lista je: ",glava);
fclose(fp);
return 0;
}