Labori materjal
Labori ülesanded
Laboris on 2 ülesannet. Enamjaolt on tegu kordamisülesannetega meenutamaks Programmeerimine 1 ainet, kuid ülesandeid on rikastatud loendite kasutamisega ning mõlema ülesande puhul saame juba kasutada eelmise nädala viitade teemat enda elu lihtsustamiseks.
Ülesanne 1: Failide kategooriad
Selles ülesandes loome tööriista, mis suudab kategoriseerida ja loendada faile vastavalt faili nimes olevale faili laiendile. Näiteks saame leida mitu dokumendifaili asub määratud kaustas ja selle alamkaustades. Ülesande raames loome vaid osa programmist, mis tegeleb laiendite tuvastamise ja loendamisega.
Failide nimede leidmiseks kasutame eelmisel semestril Linuxi laboris õpitut. Kasutades tööriista find otsime üles kõik failid rekursiivselt ning kasutades toru (pipe) suuname otsingu tulemused enda programmi loendamiseks.
NB! Lahenduse potentsiaali testimiseks on vaja Linuxit. Lihtsaim on testida seda kooli keskkonnast (st kasutades kooliarvutit, Horizongate’i või luues SSH tunneli kooli serveritesse / arvutitesse).
Nõuded
Loo programm, mis täidab järgmised nõuded
- Programm võtab vastu teadmata arvu faili nimesid standard sisendvoost (
stdin )
- Eelnevalt sisendite arvu küsimine ega erilise sisuga sõne kasutamine lugemise peatamiseks pole lubatud
- Lugemine tuleb peatada ja statistika kuvada pärast seda kui programm saab EOF (end of file, faili lõpp) signaali.
- Kategoriseeri failid vastavalt etteantud loetelule laienditest ja gruppidest.
- Näita mitu faili igasse gruppi kuulus
- Kategooriate tuvastamiseks koodis pead kasutama loendi andmetüüpi (enum)
- Üks funktsioon on sulle ette määratud. Funktsioon saab parameetrina kaasa faili laiendi sõnena ning tagastab vastava loendi väärtuse millisesse kategooriasse fail kuulub. Kasuta järgnevat prototüüpi:
enum FileCategory GetFileType(char *extension); - Programm tohib kasutajale anda informatsiooni programmi kohta (nt mida teha tuleb) pärast käivitamist. Programm ei tohi sisendite vahel ekraanile teksti väljastada (st nt kahe faili nime sisestamise vahel).
Kategooriad ja laiendid
- Arhiivid: zip, rar, 7z, tar, gz
- Andmed: csv, xls, xlsx, ods
- Dokumendid: pdf, doc, docx, rtf, odt
- Programmikood: c, h, cpp, hpp, py
- Tekst: txt
- Pildid: jpg, jpeg, png, svg,
- Muu: Kõik teised failid, millel on laiend, kuid ei olnud eelnevas loetelus.
- Laiend puudub
Koodi mall
Selleks, et sulle veidi aimdust anda kuidas lugemine ja töötlemine võiks välja näha pakun välja selleks sobiliku malli.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <stdio.h> #include <stdlib.h> // Maximum file name length in modern systems #define MAX_FILE_LEN 256 // TODO: Add enum declaration int main(void) { // Stores the name of the file char name[MAX_FILE_LEN]; // TODO: Create an array to hold the category counters // Read in the name of a file with the extension until EOF occurs while (fgets(name, MAX_FILE_LEN, stdin) != NULL) { // TODO: Call a function to fix the trailing newline // TODO: Call a function to find the position of the last point // Hint: think of what no point symbol in the string means! // TODO: Call a function that returns the category type and store it. // This is the predefined function from the requirements! // TODO: Increase the appropriate category counter } // TODO: Call a function to print the results return EXIT_SUCCESS; } |
Soovituslikud sammud ülesande lahendamisel
- Lisa koodi funktsioon, mis parandab sõne lõpus oleva reavahetuse.
Näiteks void FixTrailingNewline(char *str); - Lisa koodi funktsioon, mis leiab viimase punkti asukoha sõnas, et hiljem tuvastada faili laiendi algus selle põhjal.
Näiteks: int GetLastPointPos(char *str); - Lisa koodi failikategooria loend ning loo vastav algväärtustatud loendurite massiiv.
- Lisa koodi funktsioon loendurite massiivi väljastamiseks
- Lisa koodi funktsioon, mis leiab laiendile vastava loendi väärtuse.
Näiteks: enum FileCategory GetFileType(char *extension);
Vihjeid ja hoiatusi
- Vaata läbi täiendav loendite näide. See on üsna sarnane praegusele ülesandele ja peaks sulle andma hea idee struktuurist ja kasutusest.
- Ülesandes peaksid ära tundma tükke eelmisest semestrist – nt sõnede esimene ja teine tunnitöö, samuti vanuselise grupeerimise kodutöö.
- Kui kasutad loendi elementide väärtusteks automaatset numeratsiooni ja lisad soovitud elementide lõppu veel ühe elemendi, siis selle väärtuseks saab elementide arv loendis ilma selle viimase elemendita.
123456789101112enum FileCategory{CAT_DATA,CAT_DOCUMENTS,CAT_IMAGES,CAT_CODE,CAT_ARCHIVE,CAT_TEXT,CAT_OTHER,CAT_NO_EXT,CAT_COUNT};
Sedasi saad lihtsustada näiteks massiivide deklareerimist mille pikkus on vastavate loendurite arv.
- Kasutades viitade omadusi (nt viidaaritmeetika), saad punkti asukoha põhjal lihtsasti leida punktile järgneva tähemärgi aadressi, mis on samuti sõne ning saab olema olemuselt viit faili laiendile.
- Lugemistsükli pikkus ei ole määratud. Saad näiteks kasutada funktsiooni fgets() – see funktsioon tagastab NULL -viida kui tuvastab faili lõppu tähistava EOF (end of file) signaali.
- Meeldetuletuseks! fgets() salvestab reavahetuse sümboli sõnesse. Selle pead likvideerima!
- Kogu programmi sisend tuleb sulle läbi toru sinu programmi stdin standardvoogu.
- Kiireks testimiseks saad klaviatuurilt saata EOF signaali kasutades klahvikombinatsiooni ctrl+d .
Käsitsi testimine
Programmi käsitsi testimiseks käivitame programmi tavapäraselt ja trükime seejärel failide nimesid, vajutades iga nime järel enter klahvi. Kui soovime sisestust lõpetada, vajutame klahvikombinatsiooni ctrl+d – see saadab EOF i ehk faili lõpu signaali.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
risto.heinsar@lx9:~/P> ./task1_category a.zip b.zip a.png Archives 2 Code files 0 Data 0 Documents 0 Images 1 Text files 0 Other 0 No extension 0 |
Terviklik testimine
Selleks, et testida su programmi tööd kui terviklikult olen ette valmistanud ühe võrgukettal paikneva kausta. Sinu tulemused peaksid vastama 1:1 näitega.
Failide otsimiseks kasutame sisseehitatud tööriista find, mida kasutatakse kaustade ja failide leidmiseks. Esmalt määrame kust otsime, seejärel tüübiks failid (vältimaks kaustade nimesid) ning trükime välja vaid faili nimetuse ilma asukohata. Tulemused edastatakse toru abil sinu programmi.
Näide on käivitatud käsuga: find ~/M/risto.heinsar/lab_cat/ -type f -printf '%f\n' | ./task1_category
1 2 3 4 5 6 7 8 9 |
risto.heinsar@lx9:~/P> find ~/M/risto.heinsar/lab_cat/ -type f -printf '%f\n' | ./task1_category Archives 2 Code files 7 Data 2 Documents 3 Images 0 Text files 6 Other 1 No extension 5 |
Vihje: Mängi sellega – vaata näiteks kuidas jaotuvad failid sinu P kettal, lisa täiendavaid kategooriaid, failitüüpe.
Ülesanne 2: Pikkuste teisendaja
Sulle on edastatud andmed rahvusvahelise ettevõtte töötajate liikumisaktiivsuse kohta. Sinu ülesandeks on kõik saadud andmed teisendada ühte kasutaja poolt soovitud mõõtühikusse, seejärel kuvada tulemused ja esmane statistika.
Nõuded
- Programm võtab käsurealt 2 argumenti
- Esimene argument on faili nimi
- Teine argument on soovitud ühik milles väljundit näidata (sobilikud väljundid on m meetrites, ft jalgades ja km kilomeetrites)
- Sisendfail on lihtne tekstifail (antakse esimese käsurea argumendina)
- Iga rida failis on üks kirje
- Iga kirje koosneb kahest väljast, mis on tühikuga eraldatud: <distants> <ühik>
- Distantsid on antud reaalarvuna
- Ühikud on antud sõnena. Sisendfailis on distantsid antud ainult meetrites või jalgades.
- Leia ja kuva ühel real algne distants ja distants mis on teisendatud soovitud ühikusse (antud käsurea teise argumendina)
- Leia ja kuva kogu läbitud distants ja keskmine läbitud distants.
- Kõik pikkused kuva kahe komakohaga.
- Ühikud tuleb programmis kodeerida loendina (enum). Soovituslik loendi deklaratsioon on järgmine:
-
1enum Units {UNIT_M, UNIT_FT, UNIT_KM, UNIT_UNKNOWN};
- Teisenduse konstandid mida saad kasutada oma koodis:
123#define FT_IN_M 0.3048f#define FT_IN_MI 5280.00f#define M_IN_KM 1000.00f
Andmefailid
Selle ülesande testimiseks on kolm andmefaili. Loe täpsemalt Testimine peatüki alt mida tähele panna ning veendu tulemuste korrektsuses.
Lae andmefailid alla siit: https://blue.pri.ee/ttu/files/iax0584/andmefailid/2_2_converter_data.zip
Vihjeid
Selles ülesandes on korraga kolm erinevat ühikut (võiks olla ka rohkem!) ning nende trükkimine sedasi, et kood loetavaks jääb võib muutuda keerukaks. Kaks ideed kuidas seda lahendada.
Valik 1: Loo funktsioon ning kutsu see välja iga kord kui on vaja ühikut trükkida ekraanile. Ühik anna kaasa loendi väärtusena.
1 2 3 4 5 6 7 8 9 |
void PrintUnit(enum Units unit) { switch (unit) { case UNIT_FT: printf("ft"); break; } } |
Valik 2: Loo funktsioon, mis tagastab viite sõnele, mis omakorda sisaldab sobilikku ühikut. Kuna ühik on kirjutatud funktsioonis konstandina, siis funktsiooni eluiga probleemiks ei osutu. Sellise funktsiooni eelis on see, et saad väga mugavalt printida ühikut keerulisema printf lause sees – nt printf("%.2f %s\n, distance, ReturnPrintableUnit(unit));
1 2 3 4 5 6 7 8 |
char * ReturnPrintableUnit(enum Units unit) { switch (unit) { case UNIT_FT: return "ft"; } } |
Testimine
Selle programmiga on palju erinevaid asju mis võivad valesti minna. Testi kõiki järgnevaid olukordi!
Testid 1 – 3: Vale argumentide arv
Siin on kombineerituna näha kolm erinevat testi, kõigi nende käigus testime probleeme käivitamisel (vale argumentide arv)
1 2 3 4 5 6 7 8 9 |
risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 Invalid arguments! Usage: ./task2 data_file output_unit risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 data.txt Invalid arguments! Usage: ./task2 data_file output_unit risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 data.txt m ft Invalid arguments! Usage: ./task2 data_file output_unit |
Testid 4, 5: Probleemsed argumendid
Järgneva kahe testi vältel vaatame antud argumentide sisse. Veendume, et ühik on toetatud ning fail eksisteerib.
1 2 3 4 |
risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 data.txt mm Error: Unknown unit! risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 data.txt m Error opening input file: No such file or directory |
Test 6: tühi fail
Olukorras kus sisendfail on tühi on meie programmis samuti ohukohti. Veendume, et programm ei jookseks kokku tühja faili puhul!
task2_data1.txt sisu:
1 |
Võimalik väljund:
1 2 |
risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 task2_data1.txt m Total: 0.00 m |
Testid 7 – 9: Teisendused
Selles testis käime läbi kõik võimalikud sisendi ja väljundi kombinatsioonid mis on meil toetatud. Kasutame lihtsat andmefaili, et vastuseid parem jälgida oleks.
task2_data2.txt sisu:
1 2 |
500 m 500 ft |
Oodatav väljund:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 task2_data2.txt ft 500.00 m -> 1640.42 ft 500.00 ft -> 500.00 ft Total: 2140.42 ft Average: 1070.21 ft risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 task2_data2.txt m 500.00 m -> 500.00 m 500.00 ft -> 152.40 m Total: 652.40 m Average: 326.20 m risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 task2_data2.txt km 500.00 m -> 0.50 km 500.00 ft -> 0.15 km Total: 0.65 km Average: 0.33 km |
Test 10: Pikem fail
Selle testi puhul on peamiseks eesmärgiks proovida tulemusi pikema andmefaili korral ja veenduda, et midagi kahe silma vahele ei jäänud.
task2_data3.txt sisu:
1 2 3 4 5 6 7 8 |
1000 m 5400 m 12010 ft 850 m 4626 m 9603 ft 11215 ft 10242 m |
Oodatav väljund:
1 2 3 4 5 6 7 8 9 10 11 12 |
risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk1_enum$ ./task2 task2_data3.txt km 1000.00 m -> 1.00 km 5400.00 m -> 5.40 km 12010.00 ft -> 3.66 km 850.00 m -> 0.85 km 4626.00 m -> 4.63 km 9603.00 ft -> 2.93 km 11215.00 ft -> 3.42 km 10242.00 m -> 10.24 km Total: 32.12 km Average: 4.02 km |
Märkus! Kas panid tähele mida me ei testinud?
Edasijõudnutele: laiendatud teisendaja
Edasijõudnute ülesanne on teise laboriülesande laiendus. Mõtle ülesandest kui pikkuste teisendajast mis väljastab ka lihtlabase statistika
Nõuded
- Lisa tugi täiendavatele ühikutele
- Jard (yard, yd)
- Toll (inch, in)
- Detsimeetrid (dm, decimeter)
- Teisendused kuue toetatud ühiku vahel peavad olema toetatud mõlemas suunas (st mõlemad kõik nendest võivad olla nii sisendiks kui väljundiks)
- Lahenduse disain peab olema lihtsasti laiendatav. St täiendavate ühikute lisamine ei tohiks vajada suuremahulisi koodi ümberkirjutamisi. Teisenduse kood täiendavate ühikute lisamisel ei tohi kasvada eksponentsiaalselt!
Hoiatus: Kuigi antud ülesande oodatav lahendus on lihtsasti hallatav, tekitab see täiendavaid vigu ühikute teisenduskordajate ümardamise tõttu. Ole sellega ettevaatlik suurt täpsust nõudvates probleemides.
Pärast seda tundi peaksid
- Oskama töötada loenditega, sh
- Deklareerida uusi loendi tüüpe
- Deklareerida loendi tüüpi muutujaid
- Edastada funktsiooni ja tagastada funktsioonist loendeid.
Täiendav materjal
NB! Ettevaatust koodimisstiiliga järgnevate viidete puhul. Mitte ükski neist ei suuda isegi ühel leheküljel sama koodimisstiili reegleid jälgida!
- Enumeration (or enum) in C
https://www.geeksforgeeks.org/enumeration-enum-c/ - Enumerations
https://en.cppreference.com/w/c/language/enum - C enums
https://www.programiz.com/c-programming/c-enumeration