/* Programozási alapismeretek (levelező tagozat) projektfeladat */ /* Hallgató neve: Unger Tamás István */ /* Hallgató Neptun-kódja: FTD1YJ */ /* http://maxwell.sze.hu/~ungert */ #include #include #include #include /* A fájl neve "foglalasok.txt" és ugyanabban a mappában helyezkedik el, ahol a program maga. */ #define ADATFAJL "foglalasok.txt" /* A cég neve. Legfeljebb 20 karakter lehet, plusz a karakterláncot lezáró \0. */ #define MAXCEGNEV 21 /* A hónapok nevei. Ez a tömb egy furfangos hónapsorszám-hónapnév konverzióhoz kell majd. Az első elem dummy, hogy a C indexelési módját "becsapjuk". */ const char *Honapok[] = {"h", "januar", "februar", "marcius", "aprilis", "majus", "junius", "julius", "augusztus", "szeptember", "oktober", "november", "december"}; /* Adatstruktúra definiálása */ /* Cégnév, valamint a foglalás időpontja. Ezeket kell tárolni egy láncolt listában.*/ typedef struct Adatok{ char CegNev[MAXCEGNEV]; unsigned Ev; unsigned Honap; unsigned Nap; unsigned KezdIdo; unsigned VegIdo; struct Adatok *kovetkezo; /* Bár látszik, de tegyük világossá: a láncolt listánk egyirányú lesz, csak az elejétől lehet bejárni a végéig. */ } Adatok; /* Adatbázis adatainak beolvasása fájlból (előre megadott struktúra szerint). */ Adatok* AdatBeolvasas(){ /* Visszaadja a láncolt lista első elemére mutató mutatót. Érdemes rákukkantani a végére. Inputot nem köll neki adni. */ FILE *FajlMutato; FajlMutato = fopen(ADATFAJL, "r"); /* Olvassuk a fájlt. */ /* Megszámoljuk, hány sor (foglalás) van a fájlunkban. */ int FoglalasokSzama=0; while(fscanf(FajlMutato,"%*[^; ];%*d;%*d;%*d;%*d;%*d;\n") != EOF){ /* Olyan a format stringünk, amely illeszkedik az adatstruktúrához. Több infó: http://personal.ee.surrey.ac.uk/Personal/R.Bowden/C/scanf.html */ FoglalasokSzama++; } /* Ha nincs még foglalásunk, akkor hagyjuk a mutatót békében pihenni. */ if(FoglalasokSzama == 0){ return NULL; } /* Egyébként visszacuccoljuk az elejére. */ rewind(FajlMutato); /* Indul a láncolt lista gyártása. Jönnek a mutatók, azokat NULL-ra inicializáljuk. */ Adatok *ElsoElem=NULL; Adatok *ElozoreMutato=NULL; Adatok *AktualisElem=NULL; /* Itt történik a medzsik, feltöltjük a láncolt listát. */ int sor=0; for(sor=0;sorCegNev,&(AktualisElem->Ev),&(AktualisElem->Honap),&(AktualisElem->Nap),&(AktualisElem->KezdIdo),&(AktualisElem->VegIdo)); /* Az első elemet hozzunk létre? */ if(sor == 0 ){ ElsoElem = AktualisElem; /* Íme, a kincstári mutató. */ } else{ /* Ha ez már nem az első elem, akkor az előző elem következőre mutató mutatóját beállítjuk erre az elemre. */ ElozoreMutato->kovetkezo = AktualisElem; } /* Ha ez az utolsó elem, a következőre mutató mutatója legyen NULL. Ez zárja a láncot, konvencionális módon. */ if(sor == FoglalasokSzama-1){ AktualisElem->kovetkezo = NULL; } /* Elesszük az aktuális elem címét a következő iterációhoz. */ ElozoreMutato = AktualisElem; } fclose(FajlMutato); /* Ha kész a láncolt lista, visszaadjuk az első elemére mutató mutatót és ennyike. */ return ElsoElem; } /* Kell egy függvény, amely képes lesz kimenteni az adatokat a fájlunkba. */ int AdatMentes(Adatok *Lista){ FILE *FajlMutato; FajlMutato = fopen(ADATFAJL, "w"); /* Fontos, hogy nem append, hanem write. Kidobjuk a fájl tartalmát és újratöltjük a teljes láncolt listával. */ Adatok *SegedMutato = Lista; while(SegedMutato != NULL){ fprintf(FajlMutato,"%s;%d;%d;%d;%d;%d;\n",SegedMutato->CegNev,SegedMutato->Ev,SegedMutato->Honap,SegedMutato->Nap,SegedMutato->KezdIdo,SegedMutato->VegIdo); SegedMutato = SegedMutato->kovetkezo; } fclose(FajlMutato); printf("Adatok elmentve!\n"); return 0; } /* Ez egy, az strcmp() függvény logikája szerint működő összehasonlító függvény. A visszatérési értékének üzenetei: 1: Az első argumentumként megadott időpont nagyobb (későbbi); -1: A második argumentumként megadott időpont nagyobb (későbbi); 0: A két időpont azonos. */ int DatumHasonlit(Adatok *egyik, Adatok *masik){ if(egyik->Ev > masik->Ev){ return 1; } if(egyik->Ev < masik->Ev){ return -1; } /* Ha itt vagyunk, az évek azonosak. Nézzük a hónapot. */ if(egyik->Honap > masik->Honap){ return 1; } if(egyik->Honap < masik->Honap){ return -1; } /* Wao, a hónapok is azonosak. Nézzük a napot. */ if(egyik->Nap > masik->Nap){ return 1; } if(egyik->Nap < masik->Nap){ return -1; } /* Itt már a napok is azonosak. Nézzük a kezdési időpontokat. */ if(egyik->KezdIdo > masik->KezdIdo){ return 1; } if(egyik->KezdIdo < masik->KezdIdo){ return -1; } /* Itt azonos a két dátum. Elvileg ilyen majd nem lesz lehetséges (lásd a függvényhívást), de azért kezeljük, nehogy elröppenjen a progi. */ return 0; } /* Legyen védelem a buta user ellen. Például ha világosan számot kérünk, és beír egy stringet, akkor közöljük vele, hogy nice try, de fusson neki újra. */ void BemenetHiba(){ printf("Hiba: a megadott adat nem ertelmezheto! Kerjuk probalja ujra!"); scanf("%*s"); /* Majd nyeljük le a hibás inputot. Azért stringként, hisz' az bármi lehet. Szám is, csak máshogy kódolódik, mintha int lenne, ahogy azt megtanultuk a konzultáción. */ } /* Ezt a függvényt hívjuk meg az első menüponttal. A neve azt hiszem, hogy egyértelmű. */ /* Érdemes figyelni, hogy a függvény az adatstruktúra pointerére mutató pointert kapja meg. Így egyrészt tudjuk használni az értékét a pointernek, másrészt tudjuk azt is módosítani, hogy hova mutat. */ /* Ez veszélyes móka is lehet, de ha ügyesek vagyunk, hasznos. */ int UjFoglalas(Adatok **Lista){ char CegNev[MAXCEGNEV]; unsigned Ev, Honap, Nap, KezdIdo, VegIdo; /* Ha sikeres az új foglalás, ebbe a dobozkába fog bekerülni az ideiglenes változókból. */ Adatok *UjFoglalas; /* Újabb trükkös megoldás következik a foglaltság ellenőrzésére. Van egy 24 (0h-23h) elemű tömbünk, melynek minden elemét nullára inicializáljuk. */ /* Ha az adott indexű elem értéke 1: abban az órában foglalt a terem. Amúgy marad 0, ami a szabad jelzést jelenti. */ unsigned Foglaltsag[24]; int x; for(x=0;x<24;x++){ Foglaltsag[x]=0; } int MarFoglalt = 0; /* Bekérési interface, nincs mit részletezni rajta. Itt már pl. hívogatom a BemenetHiba() függvényt. */ int JolAdtaMeg = 1; printf("\n===UJ FOGLALAS===\n\n"); printf("Ceg neve (egy szo, max. 20 karakter, ekezetek nelkul):"); JolAdtaMeg = scanf("%s", &CegNev); printf("\nFoglalas eve (4 db szam, 2000 es 2099 kozott. pl.: 2017):"); JolAdtaMeg = scanf("%d", &Ev); /* A JolAdtaMeg változó szolgál ellenőrzésképpen, hogy a felhasználó legalább a helyes formátumot adta-e meg. /*/ if(JolAdtaMeg == 0){BemenetHiba(); return 1;} printf("\nFoglalas honapja (a honap szama, pl.: 5):"); JolAdtaMeg = scanf("%d", &Honap); /* Bővebben erről a dokumentációban fogok mesélni hely hiányában. */ if(JolAdtaMeg == 0){BemenetHiba(); return 1;} printf("\nFoglalas napja (szammal, pl.: 12):"); JolAdtaMeg = scanf("%d", &Nap); if(JolAdtaMeg == 0){BemenetHiba(); return 1;} printf("\nHany oratol (szammal 24 oras formatumban, pl.: 8):"); JolAdtaMeg = scanf("%d", &KezdIdo); if(JolAdtaMeg == 0){BemenetHiba(); return 1;} printf("\nHany oraig (szammal 24 oras formatumban, pl.: 12):"); JolAdtaMeg = scanf("%d", &VegIdo); if(JolAdtaMeg == 0){BemenetHiba(); return 1;} JolAdtaMeg = 1; printf("\n"); /* Ha ideáig eljutottunk, akkor a felhasználó az elvárt formátumban adta be az input adatokat a foglaláshoz. De még nem biztos, hogy helyesen. */ /* Ellenőrizzünk! */ /* Órák rendben? */ if(KezdIdo < 0 || KezdIdo > 23){ printf("A foglalas idopontjat hibasan adta meg. Probalja ujra!\n"); return 1; } if(VegIdo < 0 || VegIdo > 24){ printf("A foglalas idopontjat hibasan adta meg. Probalja ujra!\n"); return 1; } if(VegIdo<=KezdIdo){ printf("A foglalas idopontjat hibasan adta meg. Probalja ujra!\n"); return 1; } /* Értelmes évszámokkal próbáljunk dolgozni. */ if(Ev < 2000 || Ev > 2099){ printf("A foglalas evet hibasan adta meg. Probalja ujra!\n"); return 1; } /* Hónapok, napok rendben? */ if(Honap < 1 || Honap > 12){ printf("A foglalas honapjat hibasan adta meg. Probalja ujra!\n"); return 1; } if(Nap < 1 || Nap > 31){ printf("A foglalas napjat hibasan adta meg. Probalja ujra!\n"); return 1; } /* Nem minden hónapban van 31 nap. Ezt is ellenőrizzük. */ if(Nap == 31){ if(Honap != 1 || Honap != 3 || Honap != 5 || Honap != 7 || Honap != 8 || Honap != 10 || Honap != 12){ printf("A foglalas napjat hibasan adta meg. Probalja ujra!\n"); return 1; } } /* Szökőévek ellenőrzése. */ /* Így: https://support.microsoft.com/en-us/help/214019/method-to-determine-whether-a-year-is-a-leap-year */ if(Honap == 2 && Nap > 28){ if(Ev%4 != 0){ printf("A foglalas napjat hibasan adta meg. Probalja ujra!\n"); return 1; } } /* Ha itt vagyunk, minden OK az inputtal. Nézzük meg, hogy foglalt-e a terem. Legyen egy segédmutatónk, legyen értéke a lista eleje. */ Adatok *SegedMutato = *Lista; /* Végigkocogunk a listán egy while-al. */ while(SegedMutato != NULL){ /* Ha a megadott kérelem éppen ugyanazon a napon van, mint az aktuális elem. */ if(SegedMutato->Ev == Ev && SegedMutato->Honap == Honap && SegedMutato->Nap == Nap){ /* Akkor beállítjuk az aznapi foglalt órákat 1-re az indikátortömbben. */ for(x=SegedMutato->KezdIdo;xVegIdo;x++){ Foglaltsag[x]=1; } } /* Majd kocogunk tovább a tömbbön. */ SegedMutato = SegedMutato->kovetkezo; } /* Ha ez OK, akkor már látjuk az aznapi foglaltságot. Megnézzük a foglalási igényt, és megnézzük, hogy teljesíthető-e. */ for(x=KezdIdo;xCegNev, CegNev); UjFoglalas->Ev = Ev; UjFoglalas->Honap = Honap; UjFoglalas->Nap = Nap; UjFoglalas->KezdIdo = KezdIdo; UjFoglalas->VegIdo = VegIdo; /* Futi-futi végig a listán. */ SegedMutato = *Lista; Adatok *SegedMutatoElozo = NULL; /* Ekkor nincsen még semmi sem a listában. */ if(SegedMutato == NULL){ UjFoglalas->kovetkezo = NULL; *Lista = UjFoglalas; /* Ezért kell a kettős pointeraritmetika. Itt tesszük egyenlővé a kincstári mutatót az aktuális doboz címével. */ return 0; } /* Elindulunk végig szépen a listán. */ while(SegedMutato != NULL){ /* Ha még hátrébb kell menni, akkor kocogunk tovább. */ if(DatumHasonlit(UjFoglalas, SegedMutato) > 0){ SegedMutatoElozo = SegedMutato; SegedMutato = SegedMutato->kovetkezo; /* Ha elértünk az utolsó elemig, akkor a mögé kell beszúrni. */ if(SegedMutato == NULL){ SegedMutatoElozo->kovetkezo = UjFoglalas; UjFoglalas->kovetkezo = NULL; break; /* Ha ez teljesül, akkor kipottyanhatunk az egész listavégigjárásból. */ } continue; } /* Ha ideáig eljutottunk, akkor ide kell beszúrni a dobozunkat, mert az új foglalás ideje kevesebb, mint az aktuáis. És ki is léphetünk a ciklusból. */ else{ UjFoglalas->kovetkezo = SegedMutato; /* Ha nem az első elemnél járunk, akkor az előzőhöz hozzáláncoljuk az aktuális dobozunkat. */ if(SegedMutatoElozo != NULL){ SegedMutatoElozo->kovetkezo = UjFoglalas; } /* Ha az első elemnél járunk, akkor az elé kell berakni a dobozunkat. Módosítani kell a kincstári mutatónkat. */ else{ UjFoglalas->kovetkezo = *Lista; *Lista = UjFoglalas; } break; } } /* Ha sikeres a foglalás, szóljon a júzernek. */ printf("\n\nAz on %s nevu cegenek foglalasi igenye: %d.%d.%d., %d oratol %d oraig.\n", CegNev, Ev, Honap, Nap, KezdIdo,VegIdo); printf("A foglalas rogzitese sikeres!\n"); printf("Ne felejtse el menteni az adatbazist a kilepes elott!"); } /* Eddigi foglalások kiiratásának eljárása. */ void EddigiFoglalasok(Adatok* Lista){ printf("\n===Eddigi foglalasok===\n\n"); Adatok *SegedMutato = Lista; int SorSzam = 1; while(SegedMutato != NULL){ /* Itt történik a játék a hónapneves tömbbel, érdemes figyelni a printf argumentumát. */ printf("[%d] %s, %d. %s %d. %d - %d\n", SorSzam, SegedMutato->CegNev,SegedMutato->Ev,Honapok[(SegedMutato->Honap)],SegedMutato->Nap,SegedMutato->KezdIdo,SegedMutato->VegIdo); SegedMutato = SegedMutato->kovetkezo; SorSzam++; } } /* Foglaló cégek neveinek betűrend szerinti kiiratása következik. */ void FoglaloCegekNeve(Adatok* Lista){ printf("Foglalo cegek nevei:\n"); /* Itt is végig kell kocogni az összes rögzített foglaláson. Sajnos. */ Adatok *SegedMutato = Lista; int ListaMeret = 0; int i = 0; int s = 0; int VanMar = 0; int SorbanVan =0; /* Össze kell számolnunk, hogy hány foglalásunk van a rendszerben összesen.*/ while(SegedMutato != NULL){ ListaMeret++; SegedMutato = SegedMutato->kovetkezo; } /* Ha nincs foglalás, nem rendezünk, hisz' minek. */ if(ListaMeret == 0){ return; } /* Lesz egy tömbünk, melynek rekeszében mutatók lesznek. Ennyi darab mutató, ahány foglalásunk van a rendszerben. */ Adatok *CegNevek[ListaMeret]; /* Még egyszer végig kell menni a teljes listán és bepakolászni az egyedi neveket tartalmazó rekeszek címeit. */ SegedMutato = Lista; while(SegedMutato != NULL){ for(i=0;iCegNev, (CegNevek[i])->CegNev) == 0){ /* Ha már van cégnevünk, akkor megnézzük, hogy azonos-e az aktuálissal */ VanMar=1; /* Ha igen, jelezzük és kilépünk. */ break; } else{ VanMar=0; /* Azt is jelezzük, ha nincs. */ } } if(!VanMar){ /* Ha nincs, akkor hozzá lehet tenni a listához. */ CegNevek[s] = SegedMutato; /* Itt csapjuk hozzá a mutatót. Nem a nevet, csak az arra mutató mutatót! */ s++; } SegedMutato = SegedMutato->kovetkezo; } /* Itt jön egy csúszóshort, ami szépen sorbarendezi név szerint a cégek neveit. Nem túl gyors, de elvégzi a dolgát. */ SegedMutato = NULL; while(1){ /* Igen, ez egy végtelen ciklus. */ /* Ha csak egy elem van, nincs mit sorbarakni, végeztünk. */ if(s == 1){ break; } /* Ha egynél több elem van, akkor már van feladat. */ for(i=0;iCegNev,(CegNevek[i+1])->CegNev ) > 0){ SegedMutato = CegNevek[i]; /* Kimentjük az i-edik címet. */ CegNevek[i] = CegNevek[i+1]; /* Belerakjuk az i-edik címbe az i+1-ediket. */ CegNevek[i+1] = SegedMutato; /* Az i+1-edikbe belerakjuk a kimentett i-ediket.*/ SorbanVan = 0; /* Majd közöljük, hogy csere volt, tehát még közel sem végeztünk. */ break; /* Kilökjük magunkat a for-ból a while-ba. */ } /* Ha végigmentünk az összes elemen úgy, hogy nem volt csere: kész. */ else{ SorbanVan = 1; } } /* Ha kész, lépjünk ki a végtelen while-ból. */ if(SorbanVan){ break; } } /* Ki lehet iratni a sorban lévő neveket a sorbarendezett mutatók alapján. */ for(i=0;iCegNev); } } /* Foglalások törlése SORSZÁM szerint. */ int FoglalasTorles(Adatok **Lista){ /* Érkezik mindenféle segédváltozó. */ Adatok *SegedMutato = *Lista; int SegedSorSzam; int SorSzam; int JolAdtaMeg=0; int ListaMeret=0; /* Számoljuk meg, hogy hány foglalás van. */ while(SegedMutato != NULL){ ListaMeret++; SegedMutato = SegedMutato->kovetkezo; } /* Ha nincs foglalás, nincs mit törölni. */ if(ListaMeret == 0){ printf("Hiba: meg nincs egy foglalas sem!\n"); return 1; } /* Ha már van sorszámunk, töröljünk sorszám szerint. Egyszerűbb és életszagúbb így, sokkal. */ printf("\n===Foglalas torlese===\n\n"); printf("Foglalas sorszama (az eddigi foglalasok listajabol):"); JolAdtaMeg = scanf("%d", &SorSzam); /*Itt csak megnézzük, hogy számot adott-e. */ if(JolAdtaMeg == 0){ BemenetHiba(); return 1; /* Ha nem, szólunk a júzernek. */ } /* Most azt is megnézzük, hogy van-e ilyen számú foglalás egyáltalán. */ if(SorSzam > ListaMeret){ printf("Hiba: nincs ilyen szamu foglalas!\n"); return 1; } /* Ha igen, akkor törölhetjük a láncolt listából. */ SegedSorSzam=1; SegedMutato = *Lista; Adatok *SegedMutatoElozo = *Lista; while(SegedMutato != NULL){ if(SorSzam == SegedSorSzam){ /* Ha elérkeztünk a törlendő foglaláshoz, akkor törlünk. */ /* Közöljük a júzerrel, hogy mit. */ printf("A torlendo foglalas: [%d]: %s, %d. %s %d. %d - %d\n", SorSzam, SegedMutato->CegNev, SegedMutato->Ev, Honapok[(SegedMutato->Honap)], SegedMutato->Nap, SegedMutato->KezdIdo, SegedMutato->VegIdo); /* Ha az elsőt kell törölni, az gázosabb, mert módosul a kincstári listamutatónk értéke. */ if(SorSzam == 1){ *Lista = SegedMutato->kovetkezo; } else{ /* Egyébként csak ki kell venni a listából. */ SegedMutatoElozo->kovetkezo = SegedMutato->kovetkezo; } /* Kidobjuk a dobozt a memóriából is, mutató és lánc nélkül már úgysem találnánk meg, szemetet pedig nem tárolunk. */ free(SegedMutato); printf("A torles sikeres!\n"); break; } SegedSorSzam++; /* Amúgy pedig megyünk tovább a listán, növeljük az aktuális sorszámot. */ SegedMutatoElozo = SegedMutato; /*Eggyel odébbrajuk az előző dobozra mutató mutatót. */ SegedMutato = SegedMutato->kovetkezo; /*És az aktuális dobozra mutató mutatót is. */ } } int main(){ /* Előkészítjük a kincstári mutatót. */ Adatok *elso = NULL; /* Beolvassuk a fájlban lévő foglalásokat és visszaköpjük a láncolt lista első elemének címét. */ elso = AdatBeolvasas(); /* A main választós menüje közkedvelt fórumtéma. Én sem találtam fel a vaskarikát újra, csak a fórumok alapján felépítettem egy ugyanolyat. */ /* Ez pl. jó támpont: https://prog.hu/tudastar/133446/menu-keszitese-menupontokkal-c-ben (Egy régi Kandós házi : D) */ int Valasztas; while(1){ printf("\n\n===MENU===\n\n\n 1. Uj foglalas\n 2. Eddigi foglalasok kiirasa\n 3. Foglalo cegek neveinek kiirasa\n 4. Foglalasok mentese fajlba\n 5. Foglalas torlese\n 6. Vege\n"); while(scanf("%d", &Valasztas) != 1){ printf("Hiba: nem ertelmezheto menupont. Kerem probalja ujra!\n"); scanf("%*s"); /* le kell nyelni az ottmaradt inputot. */ } switch(Valasztas){ case 1: UjFoglalas(&elso); /*Tehát azt a címet adjuk át, ahol a dobozkánkra mutató cím címe lakik. Ez fontos, lásd 158-160. sor. */ break; case 2: EddigiFoglalasok(elso); break; case 3: FoglaloCegekNeve(elso); break; case 4: AdatMentes(elso); break; case 5: FoglalasTorles(&elso); break; case 6: return 0; break; default: printf("Hiba: nem ertelmezheto menupont. Kerem probalja ujra!\n"); break; } } }