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

