text: 2020/03/31 20:27:56
  C a C++ C++ Základní konstrukce C++ Zdrojákoviště Ošetření uživatelských vstupů
  Dnes to bude v C++ tutoriálu 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í lekce.
  cout << "Prejete si zadat dalsi priklad? [ano/ne]" << endl;
  cin >> pokracovat;
  Vstupy od uživatele bychom měli 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 tečka) 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 však není 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 použijeme funkci stoi(). Tu najdeme v knihovně string. Postup bude následující:
  požádáme uživatele o zadání hodnoty
  místo do číselné proměnné načteme vstup do stringu
  pokusíme se text převést na číslo
  v případě neúspěchu se vrátíme k bodu 1
  Jediná potíž je v tom, jak poznat neúspěch. K tomu slouží výjimky. Podrobně se jimi budeme zabývat až u objektů. Prozatím bude muset stačit to, že výjimka je způsob, jak vyslat zprávu o chybě. Pokud tuto zprávu nikde nezachytíme, ukončí program. K jejímu zachycení slouží blok try-catch. Ten vypadá takto:
  // příkazy, které mohou výjimku způsobit
  } catch (prvniTypVyjimky v1) {
  // ošetření chyby typu prvniTypVyjimky
  } catch (druhyTypVyjimky v2) {
  // ošetření výjimky typu druhyTypVyjimky
  Částí catch může být libovolný počet. Pokud místo proměnné u catch napíšeme tři tečky (...) zachytíme zde libovolnou výjimku (pokud je částí catch více, tečky smí být jen u té poslední). Jak výjimku vyvolat si vysvětlíme později, bude nám je nyní stačit umět zachytávat.
  Funkce stoi() vyvolává 2 typy výjimek:
  invalid_argument pokud nedokáže ze stringu přečíst číslo
  out_of_range pokud se přečtené číslo nevejde do typu int
  Náš kód může tedy vypadat třeba takto:
  i = stoi(s);
  catch (invalid_argument& exception)
  cout << "Nebylo zadano cislo" << endl;
  catch (out_of_range& exception)
  cout << "Cislo je prilis velke (nebo prilis male)" << endl;
  Pokud se převod nepovede, skočí se rovnou na příslušného catch bloku a příkaz break se tím pádem přeskočí. Jakmile se převod povede, vyskočíme z cyklu a pokračujeme za ním.
  Funkce pro další datové typy
  Jak vás již asi napadlo, typ int není jediným typem, který se dá ze stringu číst (tzv. parsovat). Konkrétní funkce pro načítání dalších typů máte níže v tabulce:
  float stof() 1 - 2
  double stod() 1 - 2
  long double stold() 1 - 2
  int stoi() 1 - 3
  long stol() 1 - 3
  long long stoll() 1 - 3
  unsigned long stoul() 1 - 3
  unsigned long long stoull() 1 - 3
  Kromě stringu k převedení můžeme těmto funkcím předat ještě ukazatel na typ size_t (číselný typ). Po převedení se nám do tohoto ukazatele uloží pozice, kde převod skončil. To je užitečné třeba pro čtení několika čísel oddělených např. mezerami. Pokud místo něj uvedeme nullptr, bude ignorován.
  U funkcí pro celočíselné typy můžeme uvést ještě číselnou soustavu, která se má použít. Např. čtení čísla zapsaného v šestnáctkové soustavě do typu unsigned long by tedy mohlo vypadat takto:
  unsigned long cislo = stoul(str, nullptr, 16);
  Jdeme tedy konečně upravit naši kalkulačku:
  a = stoi(s);
  cout << "Zadejte druhé číslo:" << endl;
  b = stoi(s);
  // zbytek programu
  Nyní se ještě podíváme na výběr operace a pokračování. Obě volby nyní načítáme jako string. To ale není nutné, pro výběr z menu nám stačí pouze jeden znak. Jeden znak přečteme již známou konstrukcí cin.get(). Pokud byla zadána špatná hodnota, odchytíme to v samotném switchi. Problémem je, že cin.get() přečte i enter z minulého zadání. Musíme tedy vstup opět přečíst vícekrát.
  volba = cin.get();
  } while (volba == '\n');
  cout << "Vysledek: " << (a + b) << endl;
  cout << "Vysledek: " << (a - b) << endl;
  cout << "Vysledek: " << (a * b) << endl;
  cout << "Vysledek: " << (a / b) << endl;
  cout << "Nulou nelze delit!" << endl;
  Do proměnné volba si uložíme stisknutý znak jako char. Protože se rozsah znaků nedá již tak jednoduše otestovat podmínkou, uděláme kontrolu jiným způsobem, než když jsme načítali volbu jako int. 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ů, protože se 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ž proměnnou otestovat. Vyzkoušejte si to, program se používá nyní pohodlněji.
  K tomu ještě upravíme počítání výsledku a ošetříme dělení nulou.
  Nakonec upravíme i 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í jen funkce tolower(), která převádí velká písmena na malá.
  // nacteni cisel
  cout << "Prejete si zadat dalsi priklad? [a/n]" << endl;
  bool platnaVolba = false;
  cout << "Neplatna volba, zadejte prosim a/n" << endl;
  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 napravíme a rozdělíme ho do přehledných funkcí.
  Stáhnout OsetreniUzivatelskychVstupu.zip
  Staženo 183x (8.83 kB)
  Roman:27.2.2016 18:51
  Ahoj, netreba na začiatku #include <sstream> aby:
  fungoval ???
  Lebo mne to bez toho nešlo
  Ale inak PERFECT článok
  Editováno 27.2.2016 18:52
  27.2.2016 18:51
  Jan Doskočil:18.4.2016 19:13
  nějak to pořád nechápu a to už jsem to přečetl několikrát... nemohl bych se s někým z vás domluvit na hangouts aby mi to vysvětlil? (prosíííím??)
  18.4.2016 19:13
  Mate:1.6.2017 12:05
  Ahoj, chtěl bych se zeptat, proč mi v kodů stále hlásí chybu funkce stoi() a _getch();, mám implementované tyto knihovny:
  Odpovídá na Mate
  Zdeněk Pavlátka:1.6.2017 20:07
  Ondřej Čech:17.6.2017 10:49
  Předpokládám, že jsem měl stejný problém. Zkontroluj si v kompilátoru jestli máš nastavený správný standard C++. Měl by být C++14. Mě tahle změna pomohla.
  Jaroslav Dubánek:26.8.2017 21:18
  Nefungoval mi můj kód s použitím funkcí. Stáhnul jsem tedy zip a... nic. Nefunguje mi ani ten staženej .cpp.
  26.8.2017 21:18
  David Čápka:26.8.2017 21:22
  Tato lekce je staršího data a není již součástí seriálu C++. Pokračuj prosím v seriálu C++.
  Jaroslav Dubánek:26.8.2017 21:24
  Dobrá, proto je takhle stranou. Dávám si teď opáčko toho co už znám. Díkes
  Jachym Kouba
  Jachym Kouba:18.6.2019 17:14
  Vím, že odpovídám pozdě, ale třeba to někomu pomůže. Mě to také nejdříve hlásilo chybu, ale poté jsem zadal #include <conio.h> a fungovalo to.
  Jachym Kouba:18.6.2019 19:02
  Mohl bych se zeptat, jestli by zde nemohlo být uvedeno lepší ošetření uživatelských vstupů, protože když na otázku, jestli chci další příklad, odpovím například mys, tak program napíše Neplatná volba hned třikrát. Ještě horší je, když odpovím slovem obsahujícím jedno písmeno z volby, dejme tomu pocitac, tak se zobrazí 5× Neplatná volba, poté program písmenem a odpoví na otázku a následně se to přesune na začátek a vypíše Nebylo zadáno číslo. Nějaký způsob jak toto vyřešit? Předem děkuji za odpověď.