NOVINKA - Online rekvalifikační kurz Python programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 9 - Textové řetězce v Kotlin podruhé - Práce se znaky

V minulé lekci, Nejčastější chyby Kotlin nováčků - Umíš pojmenovat proměnné?, jsme si ukázali nejčastější chyby začátečníků v Kotlinu ohledně pojmenování proměnných.

Pokud jste vycítili ve String nějakou podobnost mezi polem a textovým řetězcem, tak jste vycítili správně. Pro ostatní může být překvapením, že String je v podstatě pole znaků (hodnot typu Char) a můžeme s ním i takto pracovat.

Nejprve si vyzkoušejme, že vše funguje. Rozcvičíme se na jednoduchém vypsání znaku na dané pozici.

Klikni pro editaci
  • val s = "Kotlin"
    println(s)
    println(s[2])
    
    • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

    Výstup:

    Kotlin
    t

    Vidíme, že můžeme ke znakům v řetězci přistupovat přes hranatou závorku, jako tomu je i u pole. Zklamáním může být, že znaky na dané pozici jsou v Kotlin read-only, nemůžeme tedy napsat:

    // Tento kód nebude fungovat
    var s = "Kotlin"
    s[2] = "u"
    println(s)

    Samozřejmě to jde udělat jinak, později si to ukážeme, zatím se budeme věnovat pouze čtení jednotlivých znaků.

    Analýza výskytu znaků ve větě

    Napišme si jednoduchý program, který nám analyzuje zadanou větu. Bude nás zajímat počet samohlásek, souhlásek a počet nepísmenných znaků (např. mezera nebo !).

    Daný textový řetězec si nejprve v programu zadáme napevno, abychom ho nemuseli při každém spuštění psát. Až bude program hotový, nahradíme ho readLine(). Řetězec budeme projíždět cyklem po jednom znaku. Rovnou zde říkám, že neapelujeme na rychlost programu a budeme volit názorná a jednoduchá řešení.

    Nejprve si připravme kód, definujme si samohlásky a souhlásky. Počet ostatních znaků nemusíme počítat, bude to délka řetězce mínus samohlásky a souhlásky. Abychom nemuseli řešit velikost písmen, celý řetězec na začátku převedeme na malá písmena. Připravme si proměnné, do kterých budeme ukládat jednotlivé počty. Protože se jedná o složitější kód, nebudeme zapomínat na komentáře.

    // řetězec, který chceme analyzovat
    var s = "Mount Everest"
    println(s)
    s = s.toLowerCase()
    
    // inicializace počítadel
    var pocetSamohlasek = 0
    var pocetSouhlasek = 0
    
    // definice typů znaků
    val samohlasky = "aeiouyáéěíóúůý"
    val souhlasky = "bcčdďfghjklmnpqrřsštťvwxzž"
    
    // hlavní cyklus
    for (znak in s) {
    
    }

    Zpočátku si připravíme řetězec a převedeme ho na malá písmena. Počítadla vynulujeme. Na definice znaků nám postačí obyčejné proměnné typu String. Hlavní cyklus nám projede jednotlivé znaky v řetězci s, přičemž v každé iteraci cyklu bude v proměnné znak aktuální znak.

    Pojďme plnit počítadla, pro jednoduchost již nebudu opisovat zbytek kódu a přesunu se jen k cyklu:

    for (znak in s) {
        if (samohlasky.contains(znak)) {
            pocetSamohlasek++
        } else if (souhlasky.contains(znak)) {
            pocetSouhlasek++
        }
    }

    Metodu contains() na řetězci již známe, jako parametr ji lze předat jak podřetězec, tak přímo znak. Daný znak znak naší věty tedy nejprve zkusíme vyhledat v řetězci samohlasky a případně zvýšit jejich počítadlo. Pokud v samohláskách není, podíváme se do souhlásek a případně opětovně zvýšíme jejich počítadlo.

    Nyní nám chybí již jen výpis na konec:

    Klikni pro editaci
    • println("Samohlásek: $pocetSamohlasek")
      println("Souhlásek: $pocetSouhlasek")
      println("Nepísmenných znaků: ${s.length - (pocetSamohlasek + pocetSouhlasek)}")
      
      • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

      Výsledek:

      Mount Everest
      Samohlásek: 5
      Souhlásek: 7
      Nepísmenných znaků: 1

      A je to!

      ASCII hodnota

      Možná jste již někdy slyšeli o ASCII tabulce. Zejména v éře operačního systému MS-DOS prakticky nebyla jiná možnost, jak zaznamenávat text. Jednotlivé znaky byly uloženy jako čísla typu 1 bajt, tedy s rozsahem hodnot od 0 do 255. V systému byla uložena tzv. ASCII tabulka, která měla 256 znaků a každému ASCII kódu (číselnému kódu) přiřazovala jeden znak.

      Asi je vám jasné, proč tento způsob nepřetrval dodnes. Do tabulky se jednoduše nevešly všechny znaky všech národních abeced. Nyní se používá Unicode (UTF-8) kódování, kde jsou znaky reprezentovány trochu jiným způsobem. V Kotlin máme možnost pracovat s ASCII hodnotami jednotlivých znaků. Hlavní výhoda je v tom, že znaky jsou uloženy v tabulce za sebou, podle abecedy. Např. na pozici 97 nalezneme 'a', na 98 'b' a podobně. Podobně je to s čísly, diakritické znaky tam budou bohužel jen nějak rozházeny.

      Zkusme si nyní převést znak do jeho ASCII hodnoty a naopak podle ASCII hodnoty daný znak vytvořit.

      Klikni pro editaci
      • var c: Char // znak
        var i: Int // ordinální (ASCII) hodnota znaku
        // převedeme znak na jeho ASCII hodnotu
        c = 'a'
        i = c.toInt()
        println("Znak $c jsme převedli na ASCII hodnotu $i")
        
        // Převedeme ASCII hodnotu na znak
        i = 98
        c = i.toChar()
        println("ASCII hodnotu $i jsme převedli na znak $c")
        
        • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

        Výstup:

        Znak a jsme převedli na ASCII hodnotu 97
        ASCII hodnotu 98 jsme převedli na znak b

        Převodům (toChar() a toInt()) se říká přetypování, ale o tom se blíže pobavíme až později.

        Cézarova šifra

        Vytvoříme si jednoduchý program pro šifrování textu. Pokud jste někdy slyšeli o Cézarově šifře, bude to přesně to, co si zde naprogramujeme. Šifrování textu spočívá v posouvání znaku v abecedě o určitý, pevně stanovený počet znaků. Například slovo ahoj se s posunem textu o 1 přeloží jako bipk. Posun umožníme uživateli vybrat. Algoritmus zde máme samozřejmě opět vysvětlený a to v článku Cézarova šifra. Program si dokonce můžete vyzkoušet v praxi - Online cézarova šifra.

        Vraťme se k programování a připravme si kód. Budeme potřebovat proměnné pro původní text, zašifrovanou zprávu a pro posun. Dále cyklus projíždějící jednotlivé znaky a výpis zašifrované zprávy. Zprávu si necháme zapsanou napevno v kódu, abychom ji nemuseli při každém spuštění programu psát. Po dokončení nahradíme obsah proměnné metodou readLine(). Šifra nepočítá s diakritikou, mezerami a interpunkčními znaménky. Diakritiku budeme bojkotovat a budeme předpokládat, že ji uživatel nebude zadávat. Ideálně bychom poté měli diakritiku před šifrováním odstranit, stejně tak cokoli kromě písmen.

        // inicializace proměnných
        val s = "gaiusjuliuscaesar"
        println("Původní zpráva: $s")
        var zprava = ""
        var posun = 1
        
        // cyklus projíždějící jednotlivé znaky
        for (c in s) {
        
        }
        
        // výpis
        println("Zašifrovaná zpráva: $zprava")

        Nyní se přesuneme dovnitř cyklu, převedeme znak v c na ASCII hodnotu (neboli ordinální hodnotu), tuto hodnotu zvýšíme o posun a převedeme zpět na znak. Tento znak nakonec připojíme k výsledné zprávě:

        Klikni pro editaci
        • var i = c.toInt()
          i += posun
          val znak = i.toChar()
          zprava += znak
          
          • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

          Program si vyzkoušíme. Výsledek vypadá docela dobře. Zkusme si však zadat vyšší posun nebo napsat slovo zebra. Vidíme, že znaky mohou po 'z' přetéct do ASCII hodnot dalších znaků, v textu tedy již nemáme jen písmena, ale další ošklivé znaky. Uzavřeme znaky do kruhu tak, aby posun plynule po 'z' přešel opět k 'a' a dále. Postačí nám k tomu jednoduchá podmínka, která od nové ASCII hodnoty odečte celou abecedu tak, abychom začínali opět na 'a'.

          var i = c.toInt()
          i += posun
          if (i > 'z'.toInt()) {
              i -= 26
          }
          val znak = i.toChar()
          zprava += znak

          Pokud i přesáhne ASCII hodnotu 'z', snížíme ho o 26 znaků (tolik znaků má anglická abeceda). Operátor -= vykoná to samé, jako bychom napsali i = i - 26. Je to jednoduché a náš program je nyní funkční. Všimněme si, že nikde nepoužíváme přímé kódy znaků, v podmínce je 'z'.toInt(), i když bychom tam mohli napsat rovnou 122. Je to z důvodu, aby byl náš program plně odstíněn od explicitních ASCII hodnot a bylo lépe viditelné, jak funguje. Cvičně si zkuste udělat dešifrování.

          V následujícím cvičení, Řešené úlohy k 9. lekci Kotlin, si procvičíme nabyté zkušenosti z předchozích lekcí.


           

          Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

          Stáhnout

          Stažením následujícího souboru souhlasíš s licenčními podmínkami

          Staženo 43x (14.9 kB)
          Aplikace je včetně zdrojových kódů v jazyce Kotlin

           

          Jak se ti líbí článek?
          Před uložením hodnocení, popiš prosím autorovi, co je špatněZnaků 0 z 50-500
          Předchozí článek
          Nejčastější chyby Kotlin nováčků - Umíš pojmenovat proměnné?
          Všechny články v sekci
          Základní konstrukce jazyka Kotlin
          Přeskočit článek
          (nedoporučujeme)
          Řešené úlohy k 9. lekci Kotlin
          Článek pro vás napsal Samuel Kodytek
          Avatar
          Uživatelské hodnocení:
          41 hlasů
          Autor se věnuje všem jazykům okolo JVM. Rád pomáhá lidem, kteří se zajímají o programování. Věří, že všichni mají šanci se naučit programovat, jen je potřeba prorazit tu bariéru, který se říká lenost.
          Aktivity