Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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 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

 

Předchozí článek
Řešené úlohy k 6. lekci Dartu
Všechny články v sekci
Základní konstrukce jazyka Dart
Přeskočit článek
(nedoporučujeme)
Seznamy v Dartu
Článek pro vás napsal Honza Bittner
Avatar
Uživatelské hodnocení:
4 hlasů
FIT ČVUT alumnus :-) Sleduj mě na https://twitter.com/tenhobi a ptej se na cokoli na https://github.com/tenhobi/ama.
Aktivity