{{indexmenu_n>2}} ====== Lab 02 - Vstup a výstup programu ====== * [[courses:b3b36prg:internal:tutorialinstruction:02|pro vyučující]] /* Bude upřesněno před začátkem semestru. */ ===== Cíle cvičení ===== - Podmínky a cykly ([[courses:b3b36prg:tutorials:coding:control|řídicí struktury]]), [[https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B|operátory]]. - Standardní vstup a výstup: [[http://www.tutorialspoint.com/c_standard_library/c_function_getchar.htm|getchar]] a [[https://www.tutorialspoint.com/c_standard_library/c_function_putchar.htm|putchar]]. - Formátovaný vstup a výstup - [[http://www.tutorialspoint.com/c_standard_library/c_function_scanf.htm|scanf]],[[http://www.tutorialspoint.com/c_standard_library/c_function_printf.htm|printf]] a [[http://www.cplusplus.com/reference/cstdio/fprintf/|fprintf]]. - Ověření, že vstup byl načten a je správný. ===== Úkoly ===== ==== První cyklus ==== * Vypište několikanásobně zprávu ''I like b3b36prg!'' #include int main(void) { printf("I like b3b36prg!\n"); printf("I like b3b36prg!\n"); printf("I like b3b36prg!\n"); printf("I like b3b36prg!\n"); return 0; } který následně upravte využitím cyklu #include int main(void) { for (int i = 0; i < 4; ++i) { printf("I like b3b36prg!\n"); } return 0; } Vyzkoušejte kompilaci a spuštění programu, např. clang program.c && ./a.out nebo clang program.c -o program && ./program * V programu udělejte chybu a zkuste podmíněné spuštění programu na základě kompilace. ==== Zpracování vstupních argumentů programu ==== * Program dále upravit s hlavičkou funkce ''main'' s předáním argumentů programu, např. #include int main(int argc, char *argv[]) { for (int i = 0; i < argc; ++i) { printf("I like b3b36prg!\n"); } return 0; } * Vyzkoušejte chování programu pro spuštění s různým počtem argumentů. Kolik řádku program vypíše pro případy * ''./program'' * ''./program 1 2 3'' * ''./program a b c e'' * Počet řádků můžete spočítat nástrojem (příkazem) ''wc'' (viz ''man wc''), například ''./program 2 3 4 | wc''. ==== Standardní vstup programu ==== * Předcházející příklad rozšiřte o načtení počtu opakování načtením jednoho znaku funkcí ''getchar()'', prostudujte si dokumentaci např. ''man getchar''. int r = getchar(); for (int i; i < r; ++i) { printf("I like b3b36prg!\n"); } Je uvedný program správně? Bude vždy fungovat podle očekávání? * Vyzkoušejte chování programu pro vstup '0', //pro vstup je nutné odeslat znak (či více znaků) klávesou ''enter''//. * Kolik vypíše program řádku pro vstup ''1'', nechte spočítat přesměrovaný výstup do programu ''wc'', např. ''./a.out | wc'' a konzultujte s [[https://en.wikipedia.org/wiki/ASCII| ASCII tabulku]]. * Program upravte převodem znaku na číslíci například ''r = r - '0';'' a zkuste znovu program pro hodnoty 1 až 9. * Dále spusťte program s přesměrovaným vstupem, např. ''echo 1 | ./program''. * Vytvořte soubor se vstupem např. ''echo 3 > in.txt'', který můžete zobrazit např. ''cat in.txt'' nebo otevřít v editoru ''gedit in.txt'' nebo přímo ve VS Code ''code in.txt''. * Spusťte program s přesměrovaným vstupem ze souboru, např. ''./program < in.txt''. Edituje obsah souboru ''in.txt''. * Vytvořte prázdný soubor příkazem ''touch empty.txt'' a vyzkoušejte chování program pro takový vstup. * Program rozšiřte o výpočet návratové hodnoty programu ''return r < 0 || r > 9;'' #include int main(void) { int r = getchar(); r = r - '0'; for (int i = 0; i < r; ++i) { printf("I like b3b36prg!\n"); } return r < 0 || r > 9; } * Vyzkoušejte výstupy programu pro různé vstupy např. ./program * Diskutujte chování programu a návratovou hodnotu programu. Jak se program chová při prázdném vstupu, jakou návratovou hodnotu má funkce ''getchar()''? ==== Načtení celého čísla (více cifer) ==== Dosud jsme načítali pouze jeden znak (cifru). Pokud však chceme načíst číslo jako např. 123, musíme číst znaky jeden po druhém a postupně z nich sestavit výslednou číselnou hodnotu. * Navrhněte **algoritmus** pro načtení celého čísla ukončeného ne-cifrou; ++ příklad | 1) Inicializujeme výslednou proměnnou hodnota = 0. 2) V cyklu čteme znaky (např. pomocí getc(stdin) nebo getchar()). 2.a) Pokud je znak cifra ('0' až '9'): 2.a.1)Převedeme znak na číslo: cifra = znak - '0'. 2.a.2) Posuneme stávající hodnotu o řád výše (vynásobíme deseti) a přičteme novou cifru: hodnota = hodnota * 10 + cifra. 2.b) Pokud narazíme na jiný znak (např. mezeru nebo konec řádku), 2.b.1)čtení ukončíme. ++ * Upravte program tak, aby dokázal načíst i víceciferné číslo ze standardního vstupu a podle něj vypsal příslušný počet řádků, ++ příklad řešení:| #include int main(void) { int r = 0; int c; while ((c = getchar()) >= '0' && c <= '9') { int digit = c - '0'; r = r * 10 + digit; } for (int i = 0; i < r; ++i) { printf("%d: I like b3b36prg!\n", i + 1); } return 0; } ++ * K diskuzi: * Jak byste upravili algoritmus, aby dokázal zpracovat i záporné znaménko ''-'' na začátku vstupu? * Co se stane, když na vstup zadáte extrémně dlouhé číslo (např. 20 cifer)? * Vyzkoušejte, co se stane, pokud místo čísla pošlete prázdný vstup nebo stisknete ''Ctrl+D'' (v Linuxu/macOS) hned na začátku. /* ==== Formátovaný výstup a standardní chybový výstup ==== * Předchozí příklad rozšiřte vypsání počtu vypisovaných řádků funkcí ''printf'', např. ''printf("%d\n", r);'', viz ''man printf''. * V tomto případě bude standardní výstup vždy vypisovat jeden řádek navíc, proto použití ''wc'' nebude fungovat podle očekávání. * Program proto upravíme pro výstup počtu řádků jako ladící informace na standardní chybový výstup. #include int main(void) { int r = getchar(); r = r - '0'; for (int i = 0; i < r; ++i) { printf("I like b3b36prg!\n"); } fprintf(stderr, "%d\n", r); return r < 0 || r > 9; } * Otestujte si chování program pro různé vstup a počítání výstupu, např. ''./program out.txt''. * Přesměrujte standardní chybový výstup do souboru např. ''./program err.txt''. Obsah souborů můžete zobrazit příkazem ''cat err.txt'' a obsah aktuálního pracovního adresáře např. ''ls -l''. */ ==== Formátovaný vstup a výstup ==== * Předchozí příklad modifikujte o načtení počtu opakovaného vypsání zprávy funkcí ''scanf()'' (viz ''man scanf'') včetně ošetření chyby načtení vstupu. #include int main(void) { int ret = 0; int n; int r = scanf("%d", &n); if (r == 1) { for (int i = 0; i < n; ++i) { printf("I like b3b36prg!\n"); } } else { fprintf(stderr, "ERROR: Wrong input!\n"); ret = 100; } return ret; } * V programu můžete vložit ladící informace pro výpis načtené a návratové hodnoty. int n; int r = scanf("%d", &n); fprintf(stderr, "DEBUG: r: %d n: %d\n", r, n); * Program vyzkoušejte pro různé vstupy a počítejte řádky například příkazem ''wc'': clang program.c && echo 4 | ./a.out | wc * Program upravte tak, že bude akceptovat pouze hodnoty v rozsahu 1 až 10. V případě načtení celého čísla mimo interval vypište na ''stderr'' zprávu ''"ERROR: Out of range!\n"'' a program vrátí hodnotu ''101''. if (n >= 0 && n <= 9) { ... } else { fprintf(stderr, "ERROR: Out of range!\n"); ret = 101; } * Program zpřehledněte zavedením funkce ''void print_line(int n);'' s následující definicí. void print_line(int n) { for (int i = 0; i < n; ++i) { printf("I like b3b36prg!\n"); } } * Program vyzkoušejte pro různé kombinace vstupu včetně přesměrování a přesměrování výstupu. Vyzkoušejte program ukončení vstupu bez zadání znaku, tzv. ''EOT'' znakem ([[https://en.wikipedia.org/wiki/End-of-Transmission_character]]), který zadát kombinací ''Ctrl+D'' nebo ''Ctrl+Z'' podle použitého OS. /* ==== Procvičování formátovaného výstupu a vstupu ==== * Vyzkoušejte různé formy výpisu znaků a čísel. char c = 'a'; int i = 1000,j; float x,y = 3.1415; printf("%c\n", c); printf("%i\n", c); printf("%d\n", i); printf("%+5d\n", i); printf("%x\n", i); printf("%f\n", y); printf("%10.3f\n", y); printf("\t%-g\n", y); * Co bude výsledkem výpisu následujících příkazů? printf("%6d,%4d", 86, 1040); printf("\n"); printf("%12.5e", 3.14159265); printf("\r"); printf("%.4f\n", 85.167); * Vyzkoušejte výpis na standardní výstup (''stdout'') a standardní chybový výstup (''stderr'') funkcí ''fprintf''. fprintf(stdout, "Standard output: %d\n", i); fprintf(stderr, "Error output: %d\n", i); * Vyzkoušejte přesměrování výstupů programu do souboru. ./main >output.log 2>error.log * Vyzkoušejte načtení vstupu. 1-20.3-4.0e3 * příkazem scanf("%d%d%f%f", &i, &j, &x, &y); * Zamyslete se nad chováním příkazu ''scanf'' na základě formátovacího řetězce, otestujte chování. * Vyzkoušejte načtení vstupů " 9/ 16" a " 9 / 16" pomocí formátovacího řetězce ''"%d/%d"''. * Vyzkoušejte přesměrování vstupu programu. ./main */ ==== Další úkoly (na doma) ==== * Napište program ''out.c'', který vypíše na výstup zadaný počet řádku v rozsahu 0 až 100, kde výstup tvoří posloupnost čísel od 1 do n. * Program zkompilujte do spustitelného souboru ''out'' a vyzkoušte přesměrování výstupu do jiného programu nebo souboru, např. echo 10 | ./out echo 10 | ./out | wc echo 10 | ./out > output.txt cat output.txt wc output.txt * Napište program ''in'', který očekává na vstupu posloupnost celých čísel a po přečtení vstupu vypíše počet načtených celých čísel * V programu ošetře chybu načtení a uzavření vstupu spolu s indikací chyby návratovou hodnotou program ''100'' a ''101'' např. int r, n; while ((r = scanf("%d", &n)) == 1) { // increment of the counter } if (r == 0) { fprintf(stderr, "ERROR: Wrong input detected!\n"); ret = 100; } else { fprintf(stderr, "ERROR: Number lines %d!\n", counter); ret = 101; } * Program zkompilute do spustitelného souboru ''in'' a vyzkoušejte interaktivní režim s ručním vkládáním vstupu * Vstup ukončíte znakem ''EOT'' (End-of-Transmission) ''Ctrl+D''. * Program vyzkoušejte program pro různé vstupy i necelá čísla, ideálně se vstupem ze souboru např. echo 10 | ./out > output.txt ./in < output.txt * Programy ''out'' a ''in'' zkombinujte a ověřte jejich správné chování pro různé vstupy. * Program ''out'' upravte pro výpis celých čísel a to v podobě ''printf("%d\n", i);'' i ''printf("%d ", i);'', kde ''i'' je řidící proměnná ''for'' cyklu. * Program pak můžete řetězit např. ''echo 10 | ./out | ./in''. * Dále můžete programy rozšířit o omezení rozsahu vstupu např. na celá čísla 0 až 100 nebo maximální počet vstupních řádků.