Letní akce PHP týden
Pouze tento týden sleva až 80 % na kurzy PHP. Lze kombinovat s akcí Letní slevy na prémiový obsah!
Brno? Vypsali jsme pro vás nové termíny školení Základů programování a OOP v Brně!

Lekce 7 - Ošetření uživatelských vstupů

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Cykly v C#, 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í C# tutoriál.

Připomeňme si kód naší kalkulačky:

Console.WriteLine("Vítejte v kalkulačce");
string pokracovat = "ano";
while (pokracovat == "ano")
{
    Console.WriteLine("Zadejte první číslo:");
    float a = float.Parse(Console.ReadLine());
    Console.WriteLine("Zadejte druhé číslo:");
    float b = float.Parse(Console.ReadLine());
    Console.WriteLine("Zvolte si operaci:");
    Console.WriteLine("1 - sčítání");
    Console.WriteLine("2 - odčítání");
    Console.WriteLine("3 - násobení");
    Console.WriteLine("4 - dělení");
    int volba = int.Parse(Console.ReadLine());
    float vysledek = 0;
    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))
        Console.WriteLine("Výsledek: {0}", vysledek);
    else
        Console.WriteLine("Neplatná volba");
    Console.WriteLine("Přejete si zadat další příklad? [ano/ne]");
    pokracovat = Console.ReadLine();
}
Console.WriteLine("Děkuji za použití kalkulačky, aplikaci ukončíte libovolnou klávesou.");
Console.ReadKey();

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 místo metody Parse() použít metodu TryParse(). Metoda vrací true/false podle toho, jestli se parsování podařilo či nikoli. Jestli se ptáte, jak z metody tedy dostaneme naparsovanou hodnotu, tak ta se nám uloží do proměnné předané druhým parametrem. U parametru musíme uvést modifikátor out, zatím se jím nebudeme zatěžovat, budeme brát jako fakt, že to metoda TryParse() takhle má. Hodnota proměnné, kterou jsme takto předali do druhého parametru, bude ovlivněna. Ukážeme si to u prvního čísla, u druhého to bude samozřejmě analogické a jen to opíšeme. Ideálně bychom si na to měli vytvořit metodu, abychom nepsali 2x ten samý kód, ale zatím není vhodná doba se tímto zabývat, metody se naučíme definovat až u objektově orientovaného programování.

Console.WriteLine("Zadejte první číslo:");
float a;
while (!float.TryParse(Console.ReadLine(), out a))
    Console.WriteLine("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 TryParse(), podmínku znegujeme operátorem !. Dokud tedy vrací false, bude se cyklus stále opakovat a vyzývat k novému zadání. Zadaný text z konzole se naparsuje do proměnné a je navráceno true. Pokud se parsování nepovede, je vráceno false.

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 a potvrzovat ho enterem, stačí načíst jediný znak z klávesnice a ten nemusíme ničím potvrzovat. K načtení jediného znaku slouží nám již známá metoda Console.ReadKey(). Abychom výsledek dostali jako char (znak), musíme na této metodě použít vlastnost KeyChar. Ve switchi nezapomínejme, že char se zapisuje do apostrofů.

char volba = Console.ReadKey().KeyChar;
float vysledek = 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;
        break;
}
if (platnaVolba)
    Console.WriteLine("Výsledek: {0}", vysledek);
else
    Console.WriteLine("Neplatná volba");

Do proměnné volba si uložíme stisknutý znak jako char. 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á). Konstrukce switch zůstane podobná, jen čísla dáme nyní do apostrofů, protože se nyní jedná o jednotlivé znaky. Přidáme možnost default:, která v případě jiné hodnoty než jmenovaných nastaví námi připravenou proměnnou platnaVolba na false. Potom není nic jednoduššího, než tuto proměnnou otestovat. Vyzkoušejte si to, program se používá nyní pohodlněji.

Nakonec upravíme ještě výzvu k pokračování, zadávat budeme opět char 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 Console.ReadKey().KeyChar.ToString().ToLower(), které načte znak z konzole a vrátí ho jako string malými písmeny.

Protože se jedná o větší kus kódu, použijeme tzv. komentáře. Ty s píší pomocí dvoulomítka (dvou lomítek za sebou). Jsou to informace pro programátora, kompilátor si jich nevšímá.

Console.WriteLine("Vítejte v kalkulačce");
bool pokracovat = true;
while (pokracovat)
{
    // načtení čísel
    Console.WriteLine("Zadejte první číslo:");
    float a;
    while (!float.TryParse(Console.ReadLine(), out a))
        Console.WriteLine("Neplatné číslo, zadejte prosím znovu:");
    Console.WriteLine("Zadejte druhé číslo:");
    float b;
    while (!float.TryParse(Console.ReadLine(), out b))
        Console.WriteLine("Neplatné číslo, zadejte prosím znovu:");
    // volba operace a výpočet
    Console.WriteLine("Zvolte si operaci:");
    Console.WriteLine("1 - sčítání");
    Console.WriteLine("2 - odčítání");
    Console.WriteLine("3 - násobení");
    Console.WriteLine("4 - dělení");
    char volba = Console.ReadKey().KeyChar;
    Console.WriteLine();
    float vysledek = 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;
            break;
    }
    if (platnaVolba)
        Console.WriteLine("Výsledek: {0}", vysledek);
    else
        Console.WriteLine("Neplatná volba");
    Console.WriteLine("Přejete si zadat další příklad? [a/n]");
    // dotaz na pokračování
    platnaVolba = false;
    while (!platnaVolba)
    {
        switch (Console.ReadKey().KeyChar.ToString().ToLower())
        {
            case "a":
                pokracovat = true;
                platnaVolba = true;
            break;
            case "n":
                pokracovat = false;
                platnaVolba = true;
            break;
            default:
                Console.WriteLine("Neplatná volba, zadejte prosím a/n");
            break;
        }
    }
    Console.WriteLine();
}

Konzolová aplikace
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
Přejete si zadat další příklad? [a/n]
h
Neplatná volba, zadejte prosím a/n

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 tuto sekci 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 C# kurzu také zaměříme.

V příští lekci, Pole v C#, se opět ponoříme do nových konstrukcí. Čeká nás pole a pokročilá práce s řetězci. Potom to bude z konstrukcí v této sekci vše, blížíme se ke konci :)


 

Stáhnout

Staženo 788x (24.01 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
56 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Předchozí článek
Cykly v C#
Všechny články v sekci
Základní konstrukce jazyka C#
Miniatura
Následující článek
Pole v C#
Aktivity (9)

 

 

Komentáře
Zobrazit starší komentáře (105)

Avatar
Andy Scheuchzer:29.12.2018 10:40
// pokud je true, zadáváš pořád další a další příklady
bool pokracovat = true;
while (pokracovat)
{
    // ...
    // pokud je false, musíš zadat vstup znovu. Hodnota je přiřazena už teď, protože cyklus musí proběhnout alespoň jednou.
    platnaVolba = false;
    // všimni si vykřičníku
    while (!platnaVolba)
    {
        switch (Console.ReadKey().KeyChar.ToString().ToLower())
        {
            case "a":
                // zadal jsi platný vstup, znovu už jej zadávat nemusíš. Rozhodl ses, že chceš zadat další příklad.
                pokracovat = true;
                platnaVolba = true;
            break;
            case "n":
                // zadal jsi platný vstup (...). Rozhodl ses, že tohle byl poslední příklad.
                pokracovat = false;
                platnaVolba = true;
            break;
            default:
                // neočekávaný vstup, musíš zadat znovu
                Console.WriteLine("Neplatná volba, zadejte prosím a/n");
            break;
        }
        // zde se přejde na začátek cyklu a znovu se vyhodnotí podmínka
    }
    // zde se přejde na začátek cyklu celé kalkulačky a znovu se vyhodnotí podmínka
}

Na fb ti napsat nemůžu, takže kdyžtak soukromé zprávy zde.

Odpovědět 29.12.2018 10:40
Člověk, co si myslí, že snědl všechnu moudrost světa, i když tomu tak není.
Avatar
Martin Karel :12. února 18:52

Prosím vás, potřebuji poradit. Napsal jsem program v konzolové aplikaci, uložil ho a zavřel. Když jsem ho znovu otevřel, šla spustit pouze konzolová aplikace, ale místo příkazů mám ve VS pouze bílou plochu. Nevíte co s tím?

 
Odpovědět  -1 12. února 18:52
Avatar
Odpovídá na Martin Karel
Martin Karel :12. února 19:02

Tak už jsem na to přišel. Jen jsem přes své oči neviděl.

 
Odpovědět 12. února 19:02
Avatar
Filip Dvořáček :9. března 8:49

Ahoj,
můžu se prosím zeptat, proč je v tomto kódu před float vykřičník?

while (!float.TryParse(Console.ReadLine(), out a))
    Console.WriteLine("Neplatné číslo, zadejte prosím znovu:");

Děkuji za odpověď
Filip Dvořáček

 
Odpovědět 9. března 8:49
Avatar
Odpovídá na Filip Dvořáček
Andy Scheuchzer:9. března 10:00

Vykřičníkem zneguješ hodnotu. float.TryParse vrací, zda se převod povedl, nebo ne. Jenomže ty chceš zadat číslo znovu pouze v případě, že se nepovedl, ne naopak.

Odpovědět 9. března 10:00
Člověk, co si myslí, že snědl všechnu moudrost světa, i když tomu tak není.
Avatar
Odpovídá na Filip Dvořáček
Michal Šmahel:9. března 10:04

Ahoj, vykřičník v jakémkoliv druhu podmínky značí negaci. Negace je vlastně obrácení pravdivostní hodnoty (true --> false, false --> true).

V tomto případě jde o to, že se zkouší, zda půjde vstup převést na float - ověřuje se jeho správnost. Pokud převedení projde (metoda tryParse() vrátí true), zadaný vstup je správný a může se pokračovat dál. Jestli však nikoliv, je nutné tento stav zachytit podmínkou, upozornit uživatele a případně provést další kroky. Pokud by před float nebyl vykřičník, podmínka by byla splněna v případě, že je vstup správný. Zapotřebí je však zjištění opačného stavu, takže se to musí znegovat právě vykřičníkem.

Odpovědět  +1 9. března 10:04
Nejdůležitější je motivace, ovšem musí být doprovázena činy.
Avatar
Odpovídá na Michal Šmahel
Filip Dvořáček :10. března 8:21

Moc díky za skvělé vysvětlení

 
Odpovědět 10. března 8:21
Avatar
Honzis
Člen
Avatar
Honzis:1. května 13:50

Ahoj,
díky moc za super lekce C#. I díky tomu, že přecházím z jiného jazyka, tak zatím jedu bez problému a baví mne to. Zde jsem však narazil na něco, čemu úplně nerozumím a to konkrétně kombo: Console.ReadKe­y().KeyChar.ToS­tring().ToLower(). Jestli to dobře chápu, tak pomocí ReadKey() zadá uživatel nějaký vstup, tedy "něco," což nevím co to vlastně je, tedy jaký datový typ to vlastně je, co uživatel zadá přes ReadKey(), když to není ani char, ani string. Toto "něco" si následně převedu přes KeyChar na char a pak ještě z charu na string a s tím pak teprve pracuji pomocí ToLower(). Proč to je tak komplikované? Není nějaká jednodušší cesta? Proč se to "neco - nevím co" převádí nejprve na char a pak na string, nejde to třeba rovnou na string ? Omlouvám se, pokud jsem zde položil hloupý dotaz, ale C# se teprve učím.

 
Odpovědět 1. května 13:50
Avatar
Odpovídá na Honzis
Andy Scheuchzer:1. května 14:05

To něco je ConsoleKeyInfo. Zkus si představit, že zmáčkneš třeba šipku. To pak není žádný znak, ale klávesu jsi zmáčkl. Proč je to char? Viděl jsi už někdy, že by se při stisku jedné klávesy vložilo víc znaků než jeden? A vzhledem k tomu, že přecházíš z jiného jazyka, snad chápeš, že v takovém případě je výhodnější použít char.
ToLower() sice existuje i pro char, ale musel bys psát char.ToLower(Con­sole.ReadKey()­.KeyChar) a David Čápka se asi rozhodl, že to bude přehlednější přes ToString(). Nebo je to možná používanější, to nevím.

Odpovědět 1. května 14:05
Člověk, co si myslí, že snědl všechnu moudrost světa, i když tomu tak není.
Avatar
Honzis
Člen
Avatar
Odpovídá na Andy Scheuchzer
Honzis:1. května 14:24

To byla fakt blesková odpověď, díky :-) No to tvoje řešení mi přijde logičtější, protože jak jsi řekl, jednou klávesou mohu zadat pouze jeden znak a datový typ char je pro tento účel určený. Převádět to tedy ještě z charu na string mi přijde trošku nelogické, když string je určený pro delší řetězec, než jeden znak, ale to už se v tom asi pitvám až moc, na výsledek to očividně nemá vliv.

 
Odpovědět 1. května 14:24
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 115. Zobrazit vše