PR1ET13: Sõned

Praktikumi materjal

Esitamisele kuuluvad ülesanded

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

Ülesanne 1 [W13-1]: Tutvume string.h teegiga

Selle ülesande käigus lood samm-sammu haaval programmi, mille käigus tutvud peamiselt erinevate string.h teegis olevate standardfunktsioonidega.

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 kaks 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.
  • Kogu ülesande vältel tohid sisestuse lugemiseks ja töötlemiseks kasutada vaid ohutuid funktsioone! St funktsioone, mis piiritlevad mitu tähemärki tohib maksimaalselt lugeda.
Abifunktsioon

Selleks, et paremini aru saada mis tähemärgid parasjagu tekstimassiivi sees paiknevad, pakun välja abifunktsiooni. 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 lihtsam tuvastada, kui näiteks mõni reavahetus või muu ootamatu sümbol jääb massiivi sisse.

Juhend
1. samm: kasutaja sisestuse lugemine

Esimese sammu teeme tunnis koos läbi! Selle käigus loome kaks vajalikku funktsiooni.

Alustame esimesest, mida kasutame sõne lugemiseks. Lahenduses on oluline, et suudaksime lugeda mitmest sõnadest koosnevaid sõnesid – st tühik ei tohi 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 ohutuse tagamiseks, et puhvri pikkusest üle ei loetaks. Kolmandaks nüansiks on reavahetus, mis tekib klaviatuurilt enter klahvi vajutamisel – ka sümbol \n  salvestatakse  massiivi.

Lähenemises kasutame põhimõtet, et loome funktsioonidele wrapperid ehk ümbritseme need täiendavate lausetega, mis muudab funktsiooni kasutamise mugavamaks, säilitades turvalisuse.

Meie ümbrisel on vaja kahte sisendit – sõnet ehk tähemärgimassiivi, kuhu loetava teksti salvestame ning selle pikkust, et vältida puhvri ületäitumise rünnakuid.

Antud lahenduses olen jätnud kolme kohta küsimärgid sisse. Sinu ülesandeks on täita lüngad. Vihjeks: kui loetud sõne pikkus on 10 tähemärki, siis indeksiga 8 paikneb viimane oluline tähemärk, mille kasutaja sisestas. Sellele järgneb reavahetuse tähemärk, millest peame lahti saama, asendades selle sõne lõpu sümboliga. Vajadusel kasuta varasemalt välja toodud abifunktsiooni sisendi analüüsimiseks!

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

size_t  on andmetüüp, mida kasutatakse pikkuste hoiustamiseks ja massiivide indekseerimiseks ja loendamiseks. Tegelikkuses on see lihtsalt märgita täisarv.
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. Pikkuse leidmiseks kasuta standardfunktsiooni string.h  teegist.

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

Järgnevas näites kasutusel olev NULL  viitab objektile või mäluaadressile, mida ei eksisteeri (kutsume null-viidaks). See on vajalik kuna funktsioon strstr()  ei tagasta mitte jah/ei vastust, vaid ütleb asukoha (mäluaadressi) kus kohas otsitav sõne paikneb. Kui otsitavat sõne ei leita, tagastatakse NULL .

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 [W13]-3]: 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 [W13-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: 13_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ähegi 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 osad 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 [W13-4]: 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 [W13-5]: 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