Kapitola 7. Vstup a výstup

Obsah

7.1. Standardní vstup a výstup znaků
7.2. Standardní vstup a výstup řádků
7.3. Formátovaný standardní výstup
7.4. Formátovaný standardní vstup
7.5. Vstupní a výstupní operace v paměti
7.6. Opakování

Časová náročnost
Časová náročnost kapitoly: 1 hodina

V této části se budeme zabývat standardním textovým vstupem a výstupem a zaměříme se na možnosti jeho formátování. Každý program zpracovává nějaká vstupní data a sděluje nám výsledky touto činností získané. Pokud by tomu tak nebylo, neměli bychom zřejmě důvod takový program vůbec psát a spouštět. Vstup a výstup budeme obvykle zkráceně zapisovat I/O (Input / Output).

Důležité

Některé základní pojmy:

  • Znak je elementární textová informace. Znaku odpovídá jeho umístění v ASCII tabulce a číselný kód. Tisknutelné znaky mají kód v rozsahu 32 až 127. Mimo tento rozsah jsou umístěny buď znaky řídicí, viz například escape sekvence, nebo znaky národních abeced
  • Slovo je posloupnost znaků. Slova jsou navzájem oddělena interpunkčními znaménky a odsazovači.
  • Řádek textu je posloupnost slov ukončená symbolem (symboly) přechodu na nový řádek. Není zde žádná zmínka o délce řádku. Naše programy by se měly chovat "rozumně" pro vstupní řádky libovolné délky. Nicméně za "slušné" chování se obvykle pokládá přijetí řádku do délky nejvýše 512 znaků, jak činí mnohé unixové programy.
  • Filtry jsou programy, které čtou ze (standardního) vstupu a zapisují do (standardního) výstupu. Jde tedy o přesměrování vstupu na výstup.

Důležité

Každý program v jazyce C má při spuštění otevřen standardní vstup stdin, standardní výstup stdout a standardní chybový výstup stderr. Ty jsou obvykle směrovány na klávesnici a na terminál.

  • Pro ukončení vstupu z klávesnice použijeme v prostředí Win32 kombinaci CTRL-Z, v Unixu CTRL-D.
  • Standardní vstup a výstup používá vyrovnávací paměť obsahující jeden textový řádek. Program tedy může vstup číst až v okamžiku, kdy ukončíme řádek klávesou ENTER.
  • Při volání funkcí standardního vstupu či standardního výstupu musíme použít hlavičkový soubor stdio.h.

Vstup do programu provádíme pomocí vstupních funkcí. Tyto funkce nám umožňují zpracování vstupu buď opravdu po jednotlivých znacích, nebo po celých slovech či řádcích. Nejpokročilejší z možností vstupu je vstup formátovaný, kdy symbolicky určíme očekávaný formát vstupu a proměnnou, do níž má vstup proběhnout, a příslušná funkce takový vstup provede, nebo ohlásí chybu.

Potřebné znalosti
Potřebné znalosti
K zvládnutí obsahu této kapitoly je nutné mít znalosti z kapitoly 2 až 6 - rozumět doposud probrané látce, umět vytvářet funkce, vědět jak funkcím předávat argumenty a jak z nich získávat hodnoty zpět. Umět vytvářet rekurzivní funkce a používat cizí funkce.

7.1. Standardní vstup a výstup znaků

Časová náročnost
Časová náročnost: 7 minut

Ikdyž jsou čísla důležitá, budou naše programy často potřebovat načítat z klávesnice také znaky. Pro vstup znaků používáme getchar a putchar, případně jim odpovídající volání funkcí pracujících se standardním vstupním a výstupním proudem - getc(stdin) a putc(c, stdout).

Důležité Typ hodnoty, se kterou funkce pracují je nikoliv char, ale int. Důvodem je skutečnost, že ASCII tabulka obsahuje 256 znaků (přičemž přesně definuje dolních 128 z nich). A celkem 256 znaků je přesně tolik, kolik možností nabízí datový typ char. Vyčerpání všech kódových kombinací tohoto datového typu nedává odpovídajícím funkcím možnost pro zakódování řídicích informací. A proto je datový typ pro vstup i výstup znaků int.

Projděme si nyní syntaxi těchto příkazů:

Důležité
int getchar(void);

Přečte ze standardního vstupu jeden znak, který vrátí jako svou návratovou hodnotu. V případě chyby vrátí hodnotu EOF.

int putchar(int c);

Zadaný znak zapíše na standardní výstup. Zapsaná hodnota je současně návratovou hodnotou, v případě chyby vrací EOF.

Nyní si použití právě probraných příkazů ukažme na příkladu. Naším úkolem bude načíst znak a vytisknout jeho ASCII hodnotu.

src/getchar.c
Příklad 7.1.
/******************************
*	getchar.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 char ch;
 
 ch = getchar();   /* nacitani znaku */
 printf("Zadali jste: %d",ch);
 
 return 0;
}

7.2. Standardní vstup a výstup řádků

Časová náročnost
Časová náročnost: 7 minut

Standardní vstup a výstup řádků je jednoduchou nadstavbou nad čtením znaků.

Syntaxe:

Důležité
char *gets(char *str)
int puts(const char *str)

Tyto funkce používají hlavičkový soubor stdio.h. Funkce gets() čte zadávané znaky z klávesnice, dokud není načten znak "návrat na začátek řádku" (tj. dokud uživatel nestiskl klávesu ENTER). Přečtené znaky ukládá do pole str. Znak "návrat na začátek řádku" se k řetězci nepřidává. Místo toho je převeden na nulový ukončovací znak. Při úspěchu vrací gets() ukazatel na začátek str. Nastane-li chyba, vrací se nulový ukazatel.

Funkce puts() vypíše na obrazovku řetězec, na který ukazuje str. K řetězci se automaticky přidává sekvence "návrat na začátek řádku" a "nový řádek". Při úspěchu vrací puts() nezápornou hodnotu. Nastane-li chyba vrací se EOF.

Důležité Jednoduchost použití skrývá velké nebezpečí. Funkce gets() nemá informaci o délce oblasti vymezené pro čtený řetězec. Je-li oblast kratší, než vstupní řádek, dojde jeho načtením velmi pravděpodobně k přepsání paměťové oblasti související s vyhrazenou pamětí. A to se všemi důsledky z toho vyplývajícími. Situaci řeší funkce fgets(), která při volání vyžaduje informaci o velikosti cílového prostoru.

Správné pochopení funkcí gets() a puts() si prověříme na příkladu. Ukážeme si jak použít návratovou hodnotu funkce gets() pro přístup k řetězci obsahující zadané vstupní informace. Zároveň budeme také testovat, zda při zpracování gets() nedošlo k chybě.

src/gets.c
Příklad 7.2.
/******************************
*	gets.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 char str[80];
 
 printf("Zadejte retezec: ");
 if(gets(str))  /* kontrola zda nedoslo k chybe */
    printf("Nacteny retezec: %s", str);
 
 return 0;
}

7.3. Formátovaný standardní výstup

Časová náročnost
Časová náročnost: 12 minut

Pro formátovaný výstup používáme funkci printf(). Přesná syntaxe je následující:

Důležité
int printf (const char *format [, argument, ...]);

První argument format určuje formátovací řetězec. Ten může obsahovat popis formátu pro každý argument, nebo text, který bude zapsán do výstupu. Popis formátu vždy začíná znakem %. Chceme-li znak % použít jako text, zdvojíme jej: %%. Návratová hodnota reprezentuje počet znaků zapsaných do výstupu, nebo EOF v případě chyby. Plné určení formátu je poněkud obsáhlejší:

[flags] [width] [.prec] [l|L] type_char

Určení formátu ve funkci printf():

Důležité

Tabulka 7.1.

PoložkaVýznam
flagszarovnání výstupu, zobrazení znaménka a desetinných míst u čísel, úvodní nuly, prefix pro osmičkový a šestnáctkový výstup;
widthminimální počet znaků na výstupu, mohou být uvedeny mezerami nebo nulami;
.precmaximální počet znaků na výstupu, pro celá čísla minimum zobrazených znaků, pro racionální počet míst za desetinnou tečkou;
l-Ll indikuje dlouhé celé číslo, L long double;
type_charpovinný znak, určuje datový typ konverze.

Určení datového typu položky ve funkci printf():

Důležité

Tabulka 7.2.

SymbolVýznam
ddesítkové celé číslo se znaménkem;
udesítkové celé číslo bez znaménka;
x, Xšestnáctkové celé číslo, číslice ABCDEF malé (x) nebo velké (X);
fracionální číslo (float, double) bez exponentu, implicitně šest desetinných míst;
e, Eracionální číslo (float, double) v desetinném zápisu s exponentem, implicitně jedna pozice před desetinnou tečkou, šest za ní. Exponent uvozuje e, respektive E.
g, Gracionální číslo (float, double) v desetinném zápisu s exponentem nebo bez něj (podle absolutní hodnoty čísla). Nemusí obsahovat desetinnou tečku (nemá-li desetinnou část). Pokud je exponent menší než -4 nebo větší než počet platných číslic, je použit.
cznak;
sřetězec.

Na několika příkladech si ukážeme možnosti funkce printf(). V prvním příkladu si ukážeme možnosti tisku s určitou přesností.

src/printf_1.c
Příklad 7.3.
/******************************
*	printf_1.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 printf("%.5d\n",10);
 printf("$%.2f\n",54.32);
 printf("%.10s", "Ne vsechno co je napsane se vytiskne\n");
 
 return 0;
}

Výpis programu pak vypadá následovně:

00010
$54.32
Ne vsechno

Na dalším příkladu si ukážeme možnosti formátování. Vypíšeme hodnotu 25 čtyřmi různými způsoby: desítkově, osmičkově, šestnáctkově malými písmeny a šestnáctkově velkými písmeny. Vytiskneme také číslo v semilogaritmickém tvaru a malým ´e´ a s velkým ´E´.

src/printf_2.c
Příklad 7.4.
/******************************
*	printf_2.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 printf("%d %o %x %X\n", 25, 25, 25, 25);
 printf("%e %E\n", 25.123, 25.123);
 
 return 0;
}

Výpis programu pak vypadá následovně:

25 31 19 19
2.512300e+01 2.512300E+01

7.4. Formátovaný standardní vstup

Časová náročnost
Časová náročnost: 12 minut

Pro formátovaný standardní vstup použijeme funkci scanf() s následující syntaxí.

Důležité
int scanf (const char *format [, address, ...]);

První argument je formátovací řetězec. Obsahuje podobné možnosti popisu formátu, jako u funkce printf().

Druhý argument je paměťovou oblast (adresa paměťové oblasti), do níž bude odpovídající vstupní hodnota uložena. V praxi jde nejčastěji o adresu proměnné, nebo o ukazatel na pole znaků.

Čtení ze vstupu probíhá tak, že první formátovací popis je použit pro vstup první hodnoty, která je uložena na první adresu, druhý formátovací popis je použit pro vstup druhé hodnoty uložené na druhou adresu, ...

Návratová hodnota funkce scanf nás informuje kladným celým číslem o počtu bezchybně načtených a do paměti uložených položek, nulou o nulovém počtu uložených položek a hodnotou EOF o pokusu číst ze vstupu více položek, než v něm bylo k dispozici.

Určení datového typu položky ve funkci scanf():

Důležité

Tabulka 7.3.

SymbolVýznam
%cčtení jednoho znaku
%d, %ičtení desítkového celého čísla
%e, %f, %gčtení čísla s pohyblivou řádovou čárkou
%očtení osmičkového čísla
%sčtení řetězce
%xčtení šestnáctkového čísla
%pčtení ukazatele
%nuloží celočíselnou hodnotu, která se rovná počtu dosud načtených znaků
%učtení celého čísla bez znaménka
%[]čtení vybrané sady znaků

Následující dva programy ukáží možnosti formátovaného vstupu. V prvním programu načítáme sadu znaků, do které patří pouze malá a velká písmena. Jestliže zadáme nějaké znaky, poté třeba číslice a poté opět nějaké znaky a stiskneme ENTER budou vytištěna jenom malá a velká písmena zadaná předtím, než jste stiskli klávesu s nějakým jiným znakem než je písmeno.

src/scnf_a-z.c
Příklad 7.5.
/******************************
*	scnf_a-z.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 char str[80];
 
 printf("Zadejte znaky: \n");
 scanf("%[a-zA-Z]",str);
 
 printf(str);
 
 return 0;
}

Další program umožňuje uživateli zadat číslo následované operátorem, za kterým následuje další číslo, například 5*6. Program pak provede zadanou operaci s čísly a zobrazí výsledek.

src/scnf_opr.c
Příklad 7.6.
/******************************
*	scnf_opr.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 int i,j;
 char op;
 
 printf("Zadejte operaci: ");
 scanf("%d%c%d",&i, &op, &j);
 
 switch(op)
 {
  case '+': printf("%d",i+j);
  	   		break;
  case '-': printf("%d",i-j);
  	   		break;
  case '*': printf("%d",i*j);
  	   		break;
  case '/': printf("%d",i/j);
  	   		break;
 }
 
 return 0;
}

7.5. Vstupní a výstupní operace v paměti

Časová náročnost
Časová náročnost: 8 minut

V paměti počítače můžeme provádět prakticky stejné operace, jako při standardním vstupu a výstupu. Jen musíme specifikovat vstupní respektive výstupní řetězec. Tyto operace se provádějí pomocí funkcí sprintf() a sscanf() s následující syntaxí.

Důležité
int sprintf(char *buffer, const char *format[,argument, ...]);

int sscanf(const char *buffer, const char *format[,address, ...]);

Vstupní a výstupní řetězec je buffer, ostatní argumenty mají stejný význam jako u funkcí pro formátovaný standardní vstup a výstup.

A příklad na závěr. Máme za úkol vypsat úhly sinus a kosinus. Uživatel je vyzván, aby zadal mez, do kolika stupňů mají být obě funkce tabelovány. Ke zpracování vstupu a přípravě výstupu používáme dostatečně dimenzovaný řetězec.

src/v-v_buff.c
Příklad 7.7.
/******************************
*	v-v_buff.c
*	19.03.2002
******************************/

#include <stdio.h>
#include <math.h>

#define N 80
const od = 0;

int main(void)
{
 char b[N],
      *zahlavi = "\n x\tsin(x)\t  cos(x)";
 int x, po;
 double si, co, rad;

 printf("\nZadej do kolika stupnu:");
 gets(b);
 
 sscanf(b, "%d", &po);
 puts(zahlavi);
 for ( x = od; x < po; x++)
   {
    rad = x / 180.0 * M_PI;
    si = sin(rad);
    co = cos(rad);
    sprintf(b, "%2d %10.5f %10.5f", x, si, co);
    puts(b);
   }
 printf("\nKONEC!\a\n");
 return 0;
}

Po výzvě uživatele je jím žádaný vstup načten jako řetězec do připraveného znakového pole. Z něj je vstup zpracován pomocí funkce sscanf. Podobně je realizován výstup v těle cyklu. Řetězec je nejprve pomocí funkce sprintf naformátován do stejného pole, které již pro vstup nepotřebujeme. Pak je na výstup zapsán připravený řetězec pomocí funkce puts.

Následuje možný výstup:

zadej do kolika stupnu:3

 x      sin(x)    cos(x)
 0    0.00000    1.00000
 1    0.01745    0.99985
 2    0.03490    0.99939

KONEC!

7.6. Opakování

Cvičení
Cvičení

Úkol k textu

Zadání 1)

Napište program, který zobrazí ASCII kódy znaků AZ a az. Jak se od sebe liší kódy velkých a malých písmen?
Řešení

Úkol k textu

Zadání 2)

Je tento program správný? Pokud ne, proč?
#include <stdio.h>

int main(void)
{
 char p, *q;
 
 printf("Zadejte retezec: ");
 p = gets(q);
 printf(p);
 
 return 0;
}
Řešení

Úkol k textu

Zadání 3)

Napište program, který vypisuje tabulku čísel. Každý řádek obsahuje číslo a jeho druhou a třetí mocninu. Tabulka začíná číslem 2 a končí číslem 100.
Řešení

Úkol k textu

Zadání 4)

Jak byste vypsali tento řádek pomocí funkce printf()?
Sleva: 40% puvodni ceny
Řešení

Úkol k textu

Zadání 5)

Ukažte jak zobrazit číslo 1023.231 tak aby se vypisovala jen dvě desetinná čísla.
Řešení

Úkol k textu

Zadání 6)

Napište program, který načte desetinné číslo a pak zvlášť zobrazí jeho celou a desetinnou část.
Řešení

Úkol k textu

Zadání 7)

Je tento úsek správný? Pokud ne, proč?
char ch;
scanf("%2c", &ch);
Řešení
test7.swf
Test
Kliknutím na ikonu spustíte test.