13. labor: Sõned

Labori materjal

Esitamisele kuuluvad ülesanded

Selles tunnis on 2 ülesannet, millest esimeses õpime tundma string.h teeki ning teises manipuleerime tähemärke käsitsi.

Ülesanne 1: string.h teegiga tutvumine

Selle ülesande käigus loome ühe programmi, mille käigus tutvume peamiselt erinevate string.h teegis olevate funktsioonidega.

Nõuded
  • Programmi koostamiseks liigu samm-sammult juhendis antud järjekorras.
  • Programmi käivitades küsitakse kasutajalt parooli. Enne õige parooli sisestamist ei tohi programm edasi minna.
  • Kasutajalt loetakse lause. Programm kuvab mitu tähemärki lauses oli (sh tühikud, kirjavahemärgid).
  • Kasutajalt loetakse otsingufraas. Seejärel väljastatakse kas eelnevalt sisestatud lauses see fraas esines või mite (jah/ei vastus).
  • Kasutajalt küsitakse 2 sõna, mida kasutatakse lause moodustamisel.
    • Sõnade tüübid võid ise valida (nt nimi, ese, omadussõna, tegusõna, …)
    • Kleebi kasutaja poolt sisestatud sõnad kokku, lisades enda poolt täiendavaid sõnu moodustamaks vähemalt neljast sõnast koosneva lihtlause.
    • Üks kasutaja sisestatud sõnadest peab olema selle lauses esimene sõna.
    • Moodustatav lause tuleb salvestada täiesti uude tähemärgimassiivi, mis peab mahutama selle lause ka siis, kui loetud 2 sõna olid maksimaalse lubatud pikkusega.
Abifunktsioon

Kui sul tekib ühel või teisel hetkel probleeme aru saamisega mis sul tekstimassiivi sisuna salvestatud on, siis võid seda funktsiooni kasutada. Antud funktsioon trükib välja tervikliku sõne, misjärel trükitakse kõik tähemärgid ja selle vastavad ASCII tabeli täisarvulised väärtused. Nii on hea lihtne tuvastada kui näiteks mõni reavahetus või muu imelik sümbol jääb massiivi sisse.

Juhend
1. samm: kasutaja sisestuse lugemine

Esimese sammu teeme tunnis koos läbi! Selle käigus loome me 2 vajalikku funktsiooni oma programmi.

Alustame esimesest mida kasutame sõne lugemiseks. Lahenduses on oluline, et me suudaksime lugeda mitmetest sõnadest koosnevaid sõnesid ehk tühik ei tohiks meil lugemist ära lõpetada! Selleks on mitmeid viise, kuid meie läheneme ülesandele kasutades funktsiooni fgets() . Soovi korral võid ise teist teed minna.

Funktsiooni fgets()  eripäradeks on, et ta on mõeldud lugemaks failist. Küll aga kõik asjad on failid, sh ka klaviatuurilt tulev andmevoog, seega saame kasutada failina stdin  nimelist faili. Teine keerukus antud funktsiooni juures on see, et ta vajab endale pikkust palju tohib lugeda – see on tegelikult ohutuse tagamiseks, et puhvri pikkusest üle ei loetaks. Kolmas ja kõige probleemsem on see, et kui me vajutame klaviatuurilt enter klahvi, siis see reavahetus \n  salvestatakse samuti sinna massiivi.

Lähenemises kasutame põhimõtet, et loome funktsioonidele wrapperid ehk ümbritseme need täiendavate lausetega, mis funktsiooni kasutamise meeldivamaks või mugavamaks muudavad meie jaoks.

Meie ümbrisel on vaja kahte sisendit – sõnet ehk tähemärgimassiivi kuhu loetava teksti salvestame ning kui suur see tekstimassiiv loomise hetkel oli, et me lubatud pikkusest üle ei kirjutaks ja programmi rünnata sedasi ei lubaks.

Antud lahenduses olen jätnud kolme kohta küsimärgid sisse, kus pead ise lüngad täitma! Vihjeks: kui loetud stringi pikkus on 10 tähemärki, siis indeksiga 8 on meie jaoks viimane oluline tähemärk mille kasutaja sisestas. Sellele järgneb meile ebasoovitav reavahetus, millest peame lahti saada, asendades selle sõne lõpu sümboliga.

Vajadusel kasuta varasemalt välja toodud abifunktsiooni!

Selleks, et funktsioon ka kompileeruks olen need 2 täiendavalt vajalikku rida välja kommenteerinud. Olles küsimärgid asendanud kommenteeri need sisse.

Kui lugemine on valmis, loome järgmise ümbrise oma vastloodud GetString()  funktsioonile. Nii saame mugavalt sisestust küsida!

Nüüd kui soovime mõnda sõnet lugeda, saame oma väljakutse luua üsna mugavalt. Näiteks kui meil on tähemärgimassiiv sentence[]  mille pikkus on defineeritud makroga MAX_STR , saame väljakutse luua

2. samm: lause lugemine ja selle pikkus

Loe kasutajalt sisse lause. Seejärel leia ja väljasta sisestatud lause pikkus.

3. samm: fraasi otsimine

Lisa programmi funktsioon, milles küsitakse kasutajalt otsingufraas. Programm väljastab seepeale kas see fraas eksisteeris varasemalt sisestatud lauses või mitte.

Jah-ei vastuseks piisab kui kontrollida tagastust järgneval kujul

4. samm: parooli küsimine

Lisa programmi funktsioon, mis küsib kasutajalt parooli. Näiteks:

Parooli küsimine peab olema tsüklis ja küsima kasutajalt parooli senikaua, kuniks kasutaja sisestab korrektse parooli. Parooli kontroll peab olema tõstutundlik (st suuri ja väiketähti ei võrdsustata). Soovi korral võid panna programmi vale parooli puhul vihjeid andma või end sulgema pärast korduvalt parooli valesti sisestamist.

5. samm: lause moodustamine

Lisa programmi funktsioon, mille käigus moodustad lihtlause. Kuna funktsioonile meil head sisendit ega tagastust anda ei ole, võiksid alustada funktsiooni sedasi (void-void funktsioonid on erandlikud ja enamasti tuleb neid vältida!):

Mõttekoht: kui pikk peaks olema sentence  massiivi pikkus, et see mahutaks halvimal juhul ära mõlemad kasutaja sisestatud sõnad ning sinu lisatavad sõnad, tühikud ja kirjavahemärgid, et moodustada lauset? Suurus võib olla liigkaudne aga peab olema piisav!

Edasi mõtle välja millist lauset moodustada soovid. Oluline on, et selles lauses oleks kaks lünka, kuhu kasutaja sisestab enda soovitud sõnad (ise otsustad millised – nt inimese nimi, ese, nimisõna, tegusõna, …). Üks nendest sõnadest peab olema lause esimene sõna, teise asukoht on sinu enda otsustada. Näiteks <sõna1> on <sõna2> nimi! .

Olles kasutajalt sõnad pärinud ja programmi sisse lugenud tuleb sul need sõnad lauseks kokku kleepida. Kokku kleebitav lause peab olema salvestatud täiesti uude tühja tähemärgimassiivi. Oluline on arvestada, et mis iganes kasutaja sisestab (lubatud pikkuste raames) peab ära mahtuma sinna koostatavasse massiivi ka sellisel juhul, kui kasutaja otsustas maksimaalselt pikad sõnad sisestada.

Näide
Lisaülesanne: Tähtede loendus

Loo käsitsi uus loendamise funktsioon ning väljasta statistika

  • Loenda ja kuva mitu tähte [a-zA-Z] oli lauses. Ära loenda kirjavahemärke, tühikuid jne.
  • Leia ja kuva mitu protsenti kogu lausest moodustasid tühikud, kirjavahemärgid ja muud sümbolid.
  • Näita protsent ühe komakohaga.

Näide

Ülesanne 2: CSV-st meiliaadresside genereerimine

Selle ülesande eesmärk on sulle tutvustada laialtlevinud andmeformaati CSV (comma separated value). Ülesande lahendamise käigus saad harjutada üksikute tähemärkide tuvastamist ja töötlemist.

Lae alla ülesande aluskood: 12_2_csv_starter.c

CSV formaat

CSV on struktuursete andmete hoiustamise formaat, kus iga andmeväli on eraldatud eelnevast ja järgnevast komaga. Tegu on tõenäoliselt kõige levinuma andmete varundamiseks ja hoiustamiseks  kasutatava formaadiga väljaspool andmebaasisüsteeme. Tema peamisteks eelisteks on lihtne struktuur ning sellest tingitult on CSV toetatud praktiliselt kõigis rakendustes, mis vähekegi andmetega töötlevad.

Kõige lihtsamal kujul nagu öeldud on kõik väljad eraldatud üksteisest komaga. Näiteks:

Täpselt sellise keerukusega andmeid vaatame ka selles tunnitöös. Nägemaks keerulisemaid formaate ja reegleid kuidas hoiustada väljasid, mis peavad sisaldama komasid, jutumärke ning kuidas lisada pealkirju võid lugeda siit: https://en.wikipedia.org/wiki/Comma-separated_values#Basic_rules

Nõuded
  • Ülesande lahendus on ehitatud antud aluskoodile
  • Programm loob igale aluskoodis antud inimesele e-postiaadressi.
    • e-postiaadressi nimeosa koosneb kolmest eesnimetähest ja kolmest perenimetähest.
    • Nimeosale järgneb sinu valitud domeen.
    • E-postiaadressi nimi ja domeen peavad koosnema vaid väiketähtedest.
    • E-postiaadress tuleb salvestada terviklikult tekstimassiivi (loo uus muutuja seal kus vaja) ja väljastada selle kaudu. Jooksvalt tähemärkhaaval väljastus pole lubatud.
  • Programm väljastab:
    • Inimese täisnime. Eesnime ja perenime osa peavad olema eraldatud tühikuga
    • Genereeritud e-postiaadressi.
  • Aluskoodis juba olevat koodi ülesande lahendamiseks muuta ei tohi ilma juhendaja poolse nõusolekuta. Sinu poolt kirjutatava lahenduse alguspunkt peaks asuma  ProcessPerson()   funktsiooni sees. Soovi korral võid julgelt funktsioone juurde lisada.
Näide
Vihjed
  • Teades koma asukohta, tead ka mis indeksilt algab perenime esimene täht
  • ASCII suur- ja väiketäht erinevad üksteisest ühe biti võtta, mille järgu väärtuseks on 32 (nt A 65, a 97)
  • Kõik toimingud peale e-mailiaadressi lõpu lisamise on kõige lihtsam teha selles ülesandes tähemärk-haaval. Saab ka kasutada string.h  teegi funktsioone, kuid need võivad olla asjatult keerukad.
  • Kõige tüüpilisem viga selles ülesandes on unustada sõnele null-baidi ehk terminaatori lõppu lisamist pärast nimeosa koostamist!
Lisaülesanne 1: lühikesed nimed

Muuda oma e-postiaadresside genereerimise algoritmi sedasi, et see tuleks toime ka lühemate nimede puhul.

Näiteks: Ly Kask -> lykask@ttu.ee

Lisaülesanne 2: unikaalsed e-postiaadressid

Muuda oma e-postiaadresside genereerimise algoritmi sedasi, et see genereeriks unikaalseid postiaadresse ka siis kui nimel on sarnane algus.

Muuda oma data massiiv järgnevaks:

Nõuded

  • Loodavad e-postiaadressid peavad olema unikaalsed
  • Aadressi nimeosa peab jätkuvalt olema 6 tähemärki
  • Aadressid peavad jätkuvalt viitama nime omanikule nii palju kui võimalik
  • Täpne algoritm ja seega saavutatav nimekuju on sinu enda valida. Põhjenda valitud lahendust kaitsmisel.

Pärast tundi peaksid

  • Teadma, et tähemärkide jaoks kasutatakse erinevaid kodeeringuid, muuhulgas ASCII ja Unicode
  • Teadma mis on ASCII tabel ning kuidas seda kasutada.
  • Teadma kuidas töötavad sõned C keeles.
  • Teadma kuidas lõpetatakse sõnet C keeles (null-terminaator/null-bait).
  • Seostama C keelseid sõnesid baidijadadega.
  • Teadma mis on CSV.
  • Oskama kasutada string.h teeki sõnede manipuleerimiseks.
  • Oskama ka ise kirjutada sõnede manipulatsioone (tähemärkhaaval lähenemine).
  • Teadma mis asi on puhvri ületäitumine ning selle kaudu tehtavatest rünnakutest.

Täiendav materjal