Lekce 9 - Textové řetězce v Kotlinu 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 patříte mezi ty, kteří vycítili nějakou podobnost mezi polem a
textovým řetězcem, tak jste uvažovali správně. Pro ty ostatní může být
překvapením, že String
je v podstatě pole znaků
(hodnot typu Char
) a můžeme s ním takto i pracovat.
Nejprve si vyzkoušejme, že vše funguje. Rozcvičíme se na jednoduchém vypsání znaku na dané pozici.
{KOTLIN_CONSOLE}
val s = "Kotlin"
println(s)
println(s[2])
{/KOTLIN_CONSOLE}
Výstup:
Kotlin t
Vidíme, že ke znakům v řetězci můžeme 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 Kotlinu read-only, nemůžeme tedy napsat:
// Tento kód nebude fungovat var s = "Kotlin" s[2] = "u" println(s)
Samozřejmě to lze 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 zanalyzuje 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
řetězec funkcí readLine()
. Řetězec budeme projíždět cyklem
po jednom znaku. Upozorňujeme, že neklademe důraz 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 minus 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ž nebudeme opisovat zbytek kódu a přesuneme 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 jí
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ýšíme jejich počítadlo. Pokud znak 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:
{KOTLIN_CONSOLE}
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) {
if (samohlasky.contains(znak)) {
pocetSamohlasek++
} else if (souhlasky.contains(znak)) {
pocetSouhlasek++
}
}
println("Samohlásek: $pocetSamohlasek")
println("Souhlásek: $pocetSouhlasek")
println("Nepísmenných znaků: ${s.length - (pocetSamohlasek + pocetSouhlasek)}")
{/KOTLIN_CONSOLE}
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 neexistovala jiná možnost, jak
zaznamenávat text. Jednotlivé znaky byly uloženy jako čísla typu
byte
, 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í, v němž jsou znaky reprezentovány
trochu jiným způsobem. Nicméně v Kotlinu stále máme možnost pracovat s
ASCII hodnotami jednotlivých znaků. Hlavní výhodou je, že znaky jsou
uloženy v tabulce za sebou, podle abecedy. Např. na pozici 97
nalezneme znak 'a'
, na pozici 98
znak 'b'
a tak dále. Podobně je tomu s čísly, avšak diakritické znaky jsou v ASCII
bohužel různě rozházeny.
Zkusme si nyní převést znak do jeho ASCII hodnoty a naopak podle ASCII hodnoty daný znak vytvořit.
{KOTLIN_CONSOLE}
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")
{/KOTLIN_CONSOLE}
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.
Caesarova šifra
Vytvoříme si jednoduchý program pro šifrování textu. Pokud jste někdy
slyšeli o Caesarově š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 Caesarova
šifra. Program si dokonce můžete vyzkoušet v praxi – Online Caesarova
š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é funkcí
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. Diakritiku, stejně jako
cokoli kromě písmen, bychom poté měli v ideálním případě před
šifrováním odstranit.
// 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ě:
{KOTLIN_CONSOLE}
// 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) {
var i = c.toInt()
i += posun
val znak = i.toChar()
zprava += znak
}
// výpis
println("Zašifrovaná zpráva: $zprava")
{/KOTLIN_CONSOLE}
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 také další ošklivé znaky. Uzavřeme
proto znaky do kruhu tak, aby posun po 'z'
plynule přešel opět k
'a'
a tak 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á totéž, jako kdybychom 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()
,
přestože 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
viditelnější, jak funguje. Cvičně si zkuste vytvořit dešifrování.
V následujícím cvičení, Řešené úlohy k 9. lekci Kotlinu, 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 44x (14.9 kB)
Aplikace je včetně zdrojových kódů v jazyce Kotlin