Warning
This page is located in archive.

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

courses:b6b36pjc:cviceni:cviceni_2 [2017/09/25 17:11] (current)
richta created
Line 1: Line 1:
 +<​HTML>​
 +<​style>​
 +.code .kw1, .code .kw2, .code .kw4 { color: #00f; font-weight:​ bold; }
 +.code .br0, .code .kw3, .code .me1, .code .me2, .code .nu0 { color: #000; }
 +.code .co1, .code .coMULTI { color: #080; font-style: normal; }
 +.code .co2 { color: #888; font-style: normal; }
 +.code .st0 { color: #a31515; }
 +</​style>​
 +</​HTML>​
 +
 +===== Vstup a výstup =====
 +Cílem dnešního cvičení je seznámit se s komplikovanějším vstupem a výstupem v C%%++%%, protože program, který nekomunikuje s okolím, není zrovna užitečný.
 +
 +==== Řádkové čtení ====
 +Často je užitečné číst ze vstupu po celých řádcích, místo po řetězcích/​číslech. K tomuto účelu poslouží funkce [[http://​en.cppreference.com/​w/​cpp/​string/​basic_string/​getline|std::​getline]],​ která, jak název napovídá, čte ze vstupu, dokud nenarazí na konec řádku, a následně přečtená data uloží do ''​std::​string''​.
 +
 +Nyní bychom mohli upravit ukázku z minulého cvičení, kde chceme po uživateli, aby nám napsal své jméno, kterým ho pak pozdravíme.
 +<code cpp>
 +#include <​iostream>​
 +#include <​string>​
 +
 +int main() {
 +    std::cout << "Napis sve jmeno:​\n";​
 +    std::string jmeno;
 +    std::​getline(std::​cin,​ jmeno);
 +    std::cout << "Ahoj " << jmeno << '​\n';​
 +}
 +</​code>​
 +
 +Tentokrát načteme a uživateli vytiskneme všechen text, který napsal až do chvíle, kdy zmáčkl Enter.
 +
 +==== Ošetření chyb ====
 +Doteď jsme možnost výskytu chyb při čtení vstupu prostě ignorovali a předpokládali,​ že nám uživatel vždy dá správné vstupy. To je samozřejmě z dlouhodobého hlediska neudržitelné.
 +
 +<code cpp>
 +#include <​iostream>​
 +#include <​fstream>​
 +#include <​stdexcept>​
 +
 +double readDouble() {
 +    double d;
 +    std::cin >> d;
 +
 +    if (std::​cin.good()) {
 +        return d;
 +    }
 +    else if (std::​cin.bad() || std::​cin.eof()) {
 +        throw std::​runtime_error("​readDouble() failed"​);​
 +    }
 +    else {
 +        std::​cin.clear();​
 +        std::​cin.ignore(1,​ '​\n'​);​
 +        return readDouble();​
 +    }
 +}
 +
 +int main() {
 +    double a = readDouble();​
 +    double b = readDouble();​
 +    double c = readDouble();​
 +
 +    std::cout << (a + b + c) << '​\n';​
 +}
 +</​code>​
 +==== Formátování ====
 +
 +Tzv. I/O manipulátory nám umožňují upravit vzhled našeho výstupu.
 +
 +<code cpp>
 +#include <​iostream>​
 +#include <​iomanip>​
 +
 +void hline(int W) {
 +    for (int i = 0; i < W; ++i) {
 +        std::cout << '​-';​
 +    }
 +    std::cout << '​\n';​
 +}
 +
 +int main() {
 +    const int W = 8;
 +
 +    hline(W); ​                                   // --------
 +    std::cout << std::​setw(W) << 1234 << '​\n'; ​  // ​    1234
 +    std::cout << std::​setw(W) << 1.2 << '​\n'; ​   //      1.2
 +    std::cout << std::​setw(W) << 1.2e10 << '​\n';​ //  1.2e+10
 +    std::cout << std::​setw(W) << "​abcd"​ << '​\n';​ //     abcd
 +    hline(W); ​                                   // --------
 +    std::cout << std::hex;
 +    std::cout << std::​setw(W) << 255 << '​\n'; ​   //       ff
 +    std::cout << std::​showbase;​
 +    std::cout << std::​setw(W) << 255 << '​\n'; ​   //     0xff
 +    std::cout << std::dec;
 +    std::cout << std::​setw(W) << 255 << '​\n'; ​   //      255
 +    hline(W); ​                                   // --------
 +}
 +</​code>​
 +==== Druhy proudů ====
 +Ve standardní knihovně se dají najít 3 druhy proudů -- konzolové, souborové a řetězcové. S těmi prvními jsme se již potkali na minulém cvičení. Protože jsou proudy jedno z mála((Nevíme o dalším, ale též nenosíme v hlavě celou standardní knihovnu)) míst, kde standardní knihovna C++ používá dědičnost a virtuální funkce, typ (objekt), který se umí vypsat na konzolový výstup, se umí též vypsat do souboru nebo do řetězce. To samé samozřejmě platí i o načítání.
 +
 +=== Konzolové proudy ===
 +Konzolové proudy jsou ve standardní hlavičce ''<​iostream>''​ a existují 4:
 +
 +  * std::cin
 +  * std::cout
 +  * std::cerr
 +  * std::clog
 +
 +S prvními dvěma jsme se již setkali na minulém cvičení, s posledními dvěma ne. Dle jména není těžké si domyslet, že ''​std::​cerr''​ vypisuje na ''​stderr''​. Co ale dělá ''​std::​clog''?​ Též vypisuje na ''​stderr'',​ ale výstup bufferuje, to jest, výstup zapsaný do ''​std::​clog''​ se dostane na ''​stderr''​ až spolu s dalším výstupem, aby bylo vypisování efektivnější.
 +
 +=== Souborové proudy ===
 +Souborové proudy existují 3 a jsou ve standardní hlavičce ''<​fstream>''​.
 +
 +  * std::​ifstream
 +  * std::​ofstream
 +  * std::​fstream
 +
 +''​std::​ifstream''​ slouží ke čtení ze souboru (i - input, f - file, stream - proud). Po jeho konstrukci se s ním pracuje stejně jako se ''​std::​cin''​.
 +
 +Tato ukázka otevře soubor, který dostala jako argument, a celý ho vypíše na standardní výstup. Jedná se vlastně o (hodně) horší ''​cat''​((Klasický ''​cat''​ například hlásí chyby a bere více argumentů než jenom jeden.)).
 +<code cpp>
 +#include <​fstream>​
 +#include <​iostream>​
 +#include <​string>​
 +
 +int main(int argc, char** argv) {
 +    std::​ifstream infile(argv[1]);​
 +    std::string line;
 +    while (std::​getline(infile,​ line)) {
 +        std::cout << line << '​\n';​
 +    }
 +}
 +</​code>​
 +
 +''​std::​ofstream''​ pak slouží k zápisu do souboru (o - output, f - file, stream - proud). Po jeho konstrukci se s ním pracuje stejně jako se ''​std::​cout''​.
 +
 +Tato ukázka otevře soubor, který dostala jako argument, a zapíše do něj vše co dostane na standardním vstupu.
 +<code cpp>
 +#include <​fstream>​
 +#include <​iostream>​
 +#include <​string>​
 +
 +int main(int argc, char** argv) {
 +    std::​ofstream outfile(argv[1]);​
 +    std::string line;
 +    while (std::​getline(std::​cin,​ line)) {
 +        outfile << line << '​\n';​
 +    }
 +}
 +</​code>​
 +
 +''​std::​fstream''​ samotný pak umožňuje otevřít soubor pro zápis i pro čtení zároveň.
 +
 +
 +=== Řetězcové proudy ===
 +Existuje též řetězcový proud, ''​std::​stringstream''​((Ve skutečnosti existují zase 3 varianty, ''​std::​istringstream'',​ ''​std::​ostringstream'',​ ''​std::​stringstream'',​ ale není dobrý důvod používat jinou variantu než ''​std::​stringstream''​)),​ který umožňuje formátované čtení z/do řetězce.
 +
 +Řetězcové proudy se obvykle používají,​ pokud máme ''​std::​string''​ a chceme z něj formátovaně číst, nebo do něj dále formátovaně psát. Ukázka načte řádek ze stdin a následně ten řádek rozdělí na slova, která uloží.
 +
 +<code cpp>
 +#include <​iostream>​
 +#include <​sstream>​
 +#include <​string>​
 +#include <​vector>​
 +
 +int main(){
 +    std::string line;
 +    std::​getline(std::​cin,​ line);
 +    std::​stringstream sstream(line);​
 +    std::string word;
 +
 +    std::​vector<​std::​string>​ words;
 +    while (sstream >> word){
 +        words.push_back(word);​
 +    }
 +}
 +</​code>​
 +
 +
 +===== Nespecifikované chování =====
 +Krom nedefinovaného chování existuje i tzv. nespecifikované chování. Zatímco nedefinované chování "​nesmí nastat",​ k nespecifikovanému chování dojít může, pouze... není přesně specifikována co se má dít.
 +
 +==== Příklad 1 ====
 +Zkuste této ukázce dát nějaká data... očekává nejdříve jedno číslo, udávající množství bodů, a pak ''​n''​ bodů, [x, y], které dané body definují.
 +<code cpp>
 +#include <​iostream>​
 +#include <​vector>​
 +
 +struct point {
 +    int x, y;
 +};
 +
 +int get_int(std::​istream&​ in) {
 +    int temp;
 +    in >> temp;
 +    return temp;
 +}
 +
 +point make_point(int x, int y) {
 +    return{ x, y };
 +}
 +
 +int main() {
 +    std::​vector<​point>​ points;
 +    int n = get_int(std::​cin);​
 +    for (int i = 0; i < n; ++i) {
 +        points.push_back(make_point(get_int(std::​cin),​ get_int(std::​cin)));​
 +    }
 +
 +    for (auto& p : points) {
 +        std::cout << p.x << ' ' << p.y << std::endl;
 +    }
 +}
 +</​code>​
 +Vypsaly se vám zpátky body stejně, jako jste je načítali?
 +
 +==== Příklad 2 ====
 +Zkuste si zkompilovat a spustit následující kód. Dává výstup smysl?
 +<code cpp>
 +#include <​iostream>​
 +#include <​string>​
 +
 +int main(){
 +    std::string s = "but I have heard it works even if you don't believe in it";
 +    s.replace(0,​ 4, ""​).replace(s.find("​even"​),​ 4, "​only"​).replace(s.find("​ don'​t"​),​ 6, ""​);​
 +
 +    std::cout << s << '​\n';​
 +}
 +</​code>​
  
courses/b6b36pjc/cviceni/cviceni_2.txt · Last modified: 2017/09/25 17:11 by richta