9. diel - Textové reťazce v Dart druhýkrát - Práca s jednotlivými znakmi
V predchádzajúcom cvičení, Riešené úlohy k 7.-8. lekciu Dart, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
V milé lekcii kurzu, Riešené úlohy k 7.-8. lekciu Dart , sme sa naučili pracovať so zoznamami. Ak
ste vycítili nejakú podobnosť medzi zoznamom a textovým reťazcom, tak ste
vycítili správne. Pre ostatné môže byť prekvapením, že
String
je v podstate zoznam znakov a môžeme s
ním aj takto pracovať. Dart však, oproti iným programovacím jazykom, nemá
dátový typ, ktorý by uchovával samotný znak. V Dart je pre nás jeden znak
to isté, ako String
s dĺžkou 1.
Najprv si vyskúšajme, že to všetko funguje. Rozcvičíme sa na jednoduchom vypísanie znaku na danej pozícii:
String s = 'Ahoj ITnetwork'; print(s); print(s[2]);
Výstup programu:
Konzolová aplikácia
Ahoj ITnetwork
o
Vidíme, že môžeme ku znakom v reťazci pristupovať cez hranatú zátvorku, ako tomu je aj u zoznamu. Sklamaním môže byť, že znaky na danej pozícii sú v Dart read-only, nemôžeme teda napísať:
String s = 'Ahoj ITnetwork'; s[1] = 'o'; print(s);
Samozrejme to ide urobiť inak, neskôr si to ukážeme, zatiaľ sa budeme venovať iba čítanie jednotlivých znakov.
Analýza výskytu znakov vo vete
Napíšme si jednoduchý program, ktorý nám zanalyzuje zadanú vetu. Bude nás zaujímať počet samohlások, spoluhlások a počet nepísmenných znakov (napr. Medzera alebo!).
Daný textový reťazec si najprv v programe zadáme napevno, aby sme ho
nemuseli pri každom spustení písať. Keď bude program hotový, nahradíme ho
stdin.readLineSync()
. Reťazec budeme prechádzať cyklom po jednom
znaku - toho docielime tak, že si reťazec rozdelíme podľa vzoru '' (teda
prázdny vzor), pre ktorý nám metóda split()
vráti zoznam
reťazcov, kde každý z nich bude mať práve jeden znak. Rovno tu hovorím,
že neapeluje na rýchlosť programu a budeme voliť názorná a jednoduché
riešenia.
Najprv si pripravme kód, definujme si samohlásky a spoluhlásky. Počet nepísmen nemusíme počítať, bude to dĺžka reťazca mínus samohlásky a spoluhlásky. Aby sme nemuseli riešiť veľkosť písmen, celý reťazec na začiatku prevedieme na malé písmená. Pripravme si premenné, do ktorých budeme ukladať jednotlivé počty. Pretože sa jedná o zložitejší kód, nebudeme zabúdať na komentáre.
// řetězec, který chceme analyzovat String s = 'Programátor se zasekne ve sprše, protože instrukce na šampónu byly: Namydlit, omýt, opakovat.'; print(s); s = s.toLowerCase(); // inicializace počítadel int pocetSamohlasek = 0; int pocetSouhlasek = 0; // definice typů znaků String samohlasky = 'aeiouyáéěíóúůý'; String souhlasky = 'bcčdďfghjklmnpqrřsštťvwxzž'; // hlavní cyklus s.split('').forEach((String c) { });
Spočiatku si pripravíme reťazec a prevedieme ho na malé písmená.
Počítadla vynulujeme. Na definícia znakov nám postačí obyčajné tanga.
Hlavný cyklus nám prejde jednotlivé znaky v reťazci s
, pričom
v každej iterácii cyklu bude v premennej c
aktuálny znak.
Poďme plniť počítadla, pre jednoduchosť už nebudem opisovať zvyšok kódu a presunu sa len k cyklu:
// hlavní cyklus s.split('').forEach((String c) { if (samohlasky.contains(c)) pocetSamohlasek++; else if (souhlasky.contains(c)) pocetSouhlasek++; });
Metódu contains()
na reťazci už poznáme, ako parameter ju
možno odovzdať ľubovoľný podreťazec. Daný znak c
našej vety
teda najprv skúsime vyhľadať v reťazci samohlasky
a prípadne
zvýšiť ich počítadlo. Ak v samohláskach nie je, pozrieme sa do
spoluhlások a prípadne opätovne zvýšime ich počítadlo.
Teraz nám chýba už len výpis na koniec:
print('Samohlásek: $pocetSamohlasek'); print('Souhlásek: $pocetSouhlasek'); print('Nepísmenných znaků: ${s.length - (pocetSamohlasek + pocetSouhlasek)}');
Výpis programu:
Konzolová aplikácia
Programátor se zasekne ve sprše, protože instrukce na šampónu byly: Namydlit, omýt, opakovat.
Samohlásek: 31
Souhlásek: 45
Nepísmenných znaků: 17
A je to!
Ascii hodnota
Možno ste už niekedy počuli o ASCII tabuľke. Najmä v ére operačného
systému MS-DOS prakticky nebola iná možnosť, ako zaznamenávať text.
Jednotlivé znaky boli uložené ako čísla typu byte
, teda s
rozsahom hodnôt od 0 do 255. V systéme bola uložená tzv. ASCII tabuľka,
ktorá mala 256 znakov a každému ASCII kódu (číselnému kódu) priradzovala
jeden znak.
Asi je vám jasné, prečo tento spôsob nebol platný dodnes. Do tabuľky sa
jednoducho nevošli všetky znaky všetkých národných abecied, teraz sa
používa Unicode (UTF-8) kódovanie, kde sú znaky reprezentované trochu iným
spôsobom. V Dart máme možnosť pracovať s ASCII hodnotami jednotlivých
znakov. Hlavná výhoda je v tom, že znaky sú uložené v tabuľke za sebou,
podľa abecedy. Napr. na pozícii 97
nájdeme "a"
,
98
"b"
a podobne. Podobne je to s číslami,
diakritické znaky tam budú bohužiaľ len nejako rozhádzané.
Pre prevod budeme využívať ASCII, ktorú nájdeme v knižnici
dart:convert
.
Skúsme si teraz previesť znak do jeho ASCII hodnoty a naopak podľa ASCII hodnoty daný znak vytvoriť:
String c; // znak int i; // ordinální (ASCII) hodnota znaku // převedeme znak na jeho ASCII hodnotu c = 'a'; i = ASCII.encode(c).first; print('Znak $c jsme převedli na ASCII hodnotu $i'); // Převedeme ASCII hodnotu na znak i = 98; c = ASCII.decode([i]); print('ASCII hodnotu $i jsme převedli na znak $c');
Všimnite si, že po zakódovávání reťazca pracujeme len s prvou hodnotou
(first
), pretože funkcia pracuje všeobecne a vracia zoznam ASCII
hodnôt všetkých znakov v reťazci. Podobne tak, že pri zakódovávání
ASCII hodnoty do reťazca musíme vložiť zoznam hodnôt.
Výstup programu:
Konzolová aplikácia
Znak a jsme převedli na ASCII hodnotu 97
ASCII hodnotu 98 jsme převedli na znak b
Cézarova šifra
Vytvoríme si jednoduchý program pre šifrovanie textu. Ak ste niekedy počuli o Cézarově šifre, bude to presne to, čo si tu naprogramujeme. Šifrovanie textu spočíva v posúvaní znaku v abecede o určitý, pevne stanovený počet znakov. Napríklad slovo "ahoj" sa s posunom textu o 1 preloží ako "bipk". Posun umožníme užívateľovi vybrať. Algoritmus tu máme samozrejme opäť vysvetlený a to v článku Cézarova šifra. Program si dokonca môžete vyskúšať v praxi - Online cézarova šifra.
Vráťme sa k programovaniu a pripravme si kód. Budeme potrebovať premenné
pre pôvodné text, zašifrovanú správu a pre posun. Ďalej cyklus
prechádzajúce jednotlivé znaky a výpis zašifrované správy. Správu si
necháme zapísanú napevno v kóde, aby sme ju nemuseli pri každom spustení
programu písať. Po dokončení nahradíme obsah premennej metódou
stdin.readLineSync()
. Šifra nepočíta s diakritikou, medzier a
interpunkčných znamienok. Diakritiku budeme bojkotovať a budeme
predpokladať, že ju užívateľ nebude zadávať. Ideálne by sme potom mali
diakritiku pred šifrovaním odstrániť, rovnako tak hocičo okrem písmen.
// inicializace proměnných String s = 'cernediryjsoutamkdebuhdelilnulou'; print('Původní zpráva: $s'); String zprava = ''; int posun = 1; // cyklus projíždějící jednotlivé znaky s.split('').forEach((String c) { }); // výpis print('Zašifrovaná zpráva: $zprava');
Teraz sa presunieme dovnútra cyklu, prevedieme znak c
na ASCII
hodnotu (čiže ordinálna hodnotu), túto hodnotu zvýšime o posun a
prevedieme späť na znak. Tento znak nakoniec pripojíme k výslednej
správe:
int i = ASCII.encode(c).first; i += posun; String znak = ASCII.decode([i]); zprava += znak;
Výstup programu:
Konzolová aplikácia
Původní zpráva: cernediryjsoutamkdebuhdelilnulou
Zašifrovaná zpráva: dfsofejszktpvubnlefcviefmjmovmpv
Program si vyskúšame. Výsledok vyzerá celkom dobre. Skúsme si však zadať vyššiu posun alebo napísať slovo "zebra". Vidíme, že znaky môžu po "z" pretiecť do ASCII hodnôt ďalších znakov, v texte teda už nemáme len písmená, ale ďalšie škaredé znaky. Uzavrieme znaky do kruhu tak, aby posun plynulo po "z" prešiel opäť k "a" a ďalej. Postačí nám k tomu jednoduchá podmienka, ktorá od novej ASCII hodnoty odpočíta celú abecedu tak, aby sme začínali opäť na "a".
int i = ASCII.encode(c).first; i += posun; // kontrola přetečení if (i > ASCII.encode('z').first) i -= 26; String znak = ASCII.decode([i]); zprava += znak;
Ak i
presiahne ASCII hodnotu 'z', znížime ho o 26 znakov
(toľko znakov má anglická abeceda). Operátor -=
vykoná to
isté, ako keby sme napísali i = i - 26
. Je to jednoduché a náš
program je teraz funkčná. Všimnime si, že nikde nepoužívame priame kódy
znakov, v podmienke je ASCII.encode('z').first
, aj keď by sme tam
mohli napísať rovno 122. Je to z dôvodu, aby bol náš program plne
odtienený od explicitných ASCII hodnôt a bolo lepšie viditeľné, ako
funguje. Cvične si skúste urobiť dešifrovanie.
V budúcej lekcii, Riešené úlohy k 9. lekcii Dart , si ukážeme, že String vie predsa len ešte niečo navyše. Prezradím, že budeme dekódovať Morzeovu abecedu.
V nasledujúcom cvičení, Riešené úlohy k 9. lekcii Dart, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 4x (2.96 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Dart