Lekce 7 - Ošetření uživatelských vstupů v Dartu
V předešlém cvičení, Řešené úlohy k 6. lekci Dartu, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
V minulé lekci, Řešené úlohy k 6. lekci Dartu, jsme se zabývali cykly. Dnes to bude takové oddechové, dokončíme si totiž naši kalkulačku, dále už ji nebudeme potřebovat a bylo by hezké ji dotáhnout do konce. Asi tušíte, že u ni chybí zabezpečení vstupů od uživatele, tím se bude zabývat dnešní tutoriál.
Připomeňme si kód naší kalkulačky:
print('Vítejte v kalkulačce'); String pokracovat = 'ano'; while (pokracovat == 'ano') { print('Vítejte v kalkulačce'); print('Zadejte první číslo:'); double a = double.parse(stdin.readLineSync(encoding: UTF8)); print('Zadejte druhé číslo:'); double b = double.parse(stdin.readLineSync(encoding: UTF8)); print('Zvolte si operaci:'); print('1 - sčítání'); print('2 - odčítání'); print('3 - násobení'); print('4 - dělení'); int volba = int.parse(stdin.readLineSync(encoding: UTF8)); double vysledek; switch (volba) { case 1: vysledek = a + b; break; case 2: vysledek = a - b; break; case 3: vysledek = a * b; break; case 4: vysledek = a / b; break; } if ((volba > 0) && (volba < 5)) print('Výsledek: $vysledek'); else print('Neplatná volba'); print('Přejete si zadat další příklad? [ano/ne]'); pokracovat = stdin.readLineSync(encoding: UTF8); } print('Děkuji za použití kalkulačky.');
Už jsme si jednou říkali, že bychom měli vstupy od uživatele vždy ošetřovat. Řeknu vám tajemství úspěšných a oblíbených aplikací, je velmi jednoduché: počítají s tím, že je uživatel naprostý hlupák. Čím hloupějšího uživatele budete předpokládat, tím větší úspěch budou vaše aplikace mít. Pokud zde uživatel zadá místo "ano" např. "ano " (ano mezera) nebo "Ano" (s velkým písmenem), program stejně skončí. To ještě nemusí být kvůli hlouposti, ale proto, že se překlepl. Může nám však zadat i něco úplně nesmyslného, např. "možná".
To není však největší problém našeho programu, když uživatel nezadá číslo, ale nějaký nesmysl, celý program se zastaví a spadne s chybou. Pojďme nyní tyto dva problémy opravit.
K ověření správnosti vstupu při jeho parsování můžeme použít
metodu parse()
s přídavným parametrem onError
.
Parametr onError
očekává metodu, která nějakým způsobem
zpracuje neplatný řetězec a vrátí výslednou hodnotu.
print('Zadejte první číslo:'); double a; while ((a = double.parse(stdin.readLineSync(encoding: UTF8), (_) => null)) == null) print('Neplatné číslo, zadejte prosím znovu:');
Na kódu není nic složitého. Nejprve vyzveme uživatele k zadání čísla
a deklarujeme proměnnou a
. Následně přímo do podmínky
while
cyklu vložíme parse()
, výsledek uložíme do
proměnné a a hodnotu porovnáme vůči hodnotě null
(o tom, co
je null
a jak s hodnotou pracovat si řekneme později, zatím ji
berme jako prázdnou hodnotu). Dokud podmínka bude platit, bude se cyklus
stále opakovat a vyzývat k novému zadání.
Nyní se ještě podíváme na výběr operace a pokračování. Obě volby
načítáme jako String
i když to není úplně vhodné. U čísel
to má opodstatnění, protože mohou mít délku větší než jeden znak a
musí být odenterovány. U volby operací 1-4 ale vůbec nepotřebujeme
načítat text, stačí načíst jediný znak z klávesnice. Dart nemá
načítání z konzole vyřešeno nejlépe a kromě načtení celé řádky
(stdin.readLineSync()
) poskytuje ještě metodu
stdin.readByteSync()
k načtení jediného znaku (konkrétněji asi
bytu). Jelikož nám vrátí jen hondnotu znaku, musíme jej ještě například
pomocí ASCII dekodéru převést na znak, kdy je v tomto řešení problém
např. s diakritikou. Otázka u této metody ale také je, jak se přesně bude
chovat na různých operačních systémech a jestli nám vždy opravdu načte
celý znak. Metodu si vyzkoušíme, ale dále budeme načítat celý řádek a
hodnoty si parsovat jako doposud.
String volba = ASCII.decode([stdin.readByteSync()]); bool platnaVolba = true; double vysledek; switch (volba) { case '1': vysledek = a + b; break; case '2': vysledek = a - b; break; case '3': vysledek = a * b; break; case '4': vysledek = a / b; break; default: platnaVolba = false; } if (platnaVolba) print('Výsledek: $vysledek'); else print('Neplatná volba');
Do proměnné volba
si uložíme stisknutý znak jako
String
. Protože rozsah znaků neotestujeme s dosavadními
znalostmi tak jednoduše jako rozsah čísel, pomůžeme si jiným způsobem.
Připravíme si proměnnou platnaVolba
typu bool
,
kterou nastavíme na true
(budeme předpokládat, že je volba
správná). Switch zůstane podobný, jen čísla dáme nyní do apostrofů či
uvozovek. Přidáme možnost default
, která v případě jiné
hodnoty než jmenované nastaví námi připravenou proměnnou
platnaVolba
na false
. Potom není nic jednoduššího,
než tuto proměnnou otestovat. Vyzkoušejte si to. Určitě si všimnete
další "chyby" při používání stdin.readByteSync()
a to sice
toho, že přečte náš znak, ale nechá nám na vstupu odřádkování, což
následně dotaz na opakování kalkulačky špatně vyhodnotí a program nám
ukončí.
Program si tedy upravíme do původní podoby. Upravit musíme ještě výzvu
k pokračování. Zadávat budeme opět A/N, budeme tolerovat různou velikost
písmen a reagovat na špatné zadání. Opět použijeme switch
,
naši proměnnou pokracovat
změníme na typ bool
.
Kód je asi zbytečné více popisovat, za zmínku stojí pouze kombo
stdin.readLineSync(encoding: UTF8).trim().toLowerCase()
, které
načte vstup z konzole a vrátí ho jako String
malými písmeny a
bez počátečních či ukončovacích mezer.
Protože se jedná o větší kus kódu, použijeme tzv. komentáře. Ty se píší pomocí dvojlomítka (dvou lomítek za sebou). Jsou to informace pro programátora, program si jich nevšímá.
print('Vítejte v kalkulačce'); bool pokracovat = true; while (pokracovat) { // načtení čísel print('Zadejte první číslo:'); double a; while ((a = double.parse(stdin.readLineSync(encoding: UTF8), (_) => null)) == null) print('Neplatné číslo, zadejte prosím znovu:'); print('Zadejte druhé číslo:'); double b; while ((b = double.parse(stdin.readLineSync(encoding: UTF8), (_) => null)) == null) print('Neplatné číslo, zadejte prosím znovu:'); // volba operace a výpočet print('Zvolte si operaci:'); print('1 - sčítání'); print('2 - odčítání'); print('3 - násobení'); print('4 - dělení'); int volba = int.parse(stdin.readLineSync(encoding: UTF8)); double vysledek = 0.0; bool platnaVolba = true; switch (volba) { case 1: vysledek = a + b; break; case 2: vysledek = a - b; break; case 3: vysledek = a * b; break; case 4: vysledek = a / b; break; default: platnaVolba = false; } if (platnaVolba) print('Výsledek: $vysledek'); else print('Neplatná volba'); print('Přejete si zadat další příklad? [a/n]'); // dotaz na pokračování platnaVolba = false; while (!platnaVolba) { switch (stdin.readLineSync(encoding: UTF8).trim().toLowerCase()) { case 'a': pokracovat = true; platnaVolba = true; break; case 'n': pokracovat = false; platnaVolba = true; break; default: print('Neplatná volba, zadejte prosím a/n'); break; } } } print('Děkuji za použití kalkulačky.');
Výstup programu:
Konzolová aplikace
Vítejte v kalkulačce
Zadejte první číslo:
cislo
Neplatné číslo, zadejte prosím znovu:
13
Zadejte druhé číslo:
22
Zvolte si operaci:
1 - sčítání
2 - odčítání
3 - násobení
4 - dělení
3
Výsledek: 286.0
Přejete si zadat další příklad? [a/n]
n
Děkuji za použití kalkulačky.
Gratuluji, právě jste vytvořili svůj první blbovzdorný program Kód se nám trochu zkomplikoval, snad jste to všechno pochytili. Někdy v budoucnu to třeba napravíme a rozdělíme ho do přehledných metod, pro tento kurz však považujme kalkulačku za hotovou, možná by se do ní jen mohlo přidat více matematických funkcí, na ty se v kurzu také zaměříme.
V příští lekci, Seznamy v Dartu, se opět ponoříme do nových konstrukcí. Čekají nás seznamy a pokročilá práce s řetězci. Potom to bude z konstrukcí v této sekci vše, blížíme se ke konci.
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 8x (2.81 kB)
Aplikace je včetně zdrojových kódů v jazyce Dart