Učebnica

per aspera ad astra

Nástroje používateľa

Nástoje správy stránok


kruzok:jazyk_c:standardny_vstup_a_vystup

Štandardný vstup a výstup

Teraz na chvíľu odbočíme od výkladu syntaxe jazyka C a pozrieme sa na dve funkcie: printf() a jej sestričku scanf().

Funkciu printf() používame už od prvého programu (a budeme používať aj naďalej) a tak sa sluší riadne popísať jej možnosti. V súvislosti s týmito funkciami si tiež objasníme pojmy štandardný vstup a výstup. Len pripomínam, že s funkciou printf() úzko súvisí tzv. escape sekvenciami prebranými v minulej kapitole.

Štandardný vstup a výstup

Každý program pri svojom spustení dostane od operačného systému štandardný vstup (obvykle klávesnicu), štandardný výstup (obvykle monitor) a štandardný chybový výstup (obvykle monitor). Štandardný vstup sa označuje ako stdin, štandardný výstup ako stdout a chybový výstup je stderr.

Funkcie, ktoré s týmito vstupmi a výstupmi pracujú sú definované v hlavičkovom súbore <stdio.h>. Napríklad funkcia printf() tlačí všetko do štandardného výstupu stdout. V OS máme možnosť štandardné vstupy a výstupy presmerovať (napríklad výstup do súboru namiesto na obrazovku). Ak používate Linux, určite vám je známe presmerovanie štandardného výstupu pomocou znaku > a chybového výstupu pomocou znakov 2>. Ak sa vstup alebo výstup presmeruje, program to de facto ani nespozoruje (nie že by to nešlo zistiť) a tak sa tým nemusíte nijako zaťažovať.

Vstupné a výstupné zariadenia možno rozdeliť na znakové a blokové. Znakové sú napríklad klávesnica a monitor, blokové sú napr. pevné disky. Rozdiel je predovšetkým v možnosti získavania či vkladanie dát z / do zariadenia. Ku znakovým zariadením sa pristupuje sekvenčne teda znak po znaku a k blokovým tzv. náhodným prístupom (random access). Znamená to, že z blokových zariadení môžete pristupovať k dátam podľa ľubovôle (môžete čítať z ktorejkoľvek časti disku, alebo do ktorejkoľvek časti zapisovať), kdežto u sekvenčných zariadenia možno čítať alebo zapisovať iba po sekvenciách (napr. na monitor alebo tlačiareň je možné zapisovať iba riadok za riadkom (aspoň skôr to nebolo možné inak) a z klávesnice môžete čítať len tie znaky, ktoré užívateľ práve stlačil (tie, ktoré stlačil pred minútou alebo stlačí až za minútu nezistíte)).

Funkcia printf()

Funkcia printf() sa používa pre formátovaný štandardný výstup (stdout). Deklarácia funkcie printf() vyzerá takto:

int printf (const char * format [, arg, ...]);

Niečo málo o argumentoch a návratových hodnotách funkcií sme už hovorili v súvislosti s funkciou main(). V rovnakej kapitole som hovoril tiež o funkcii printf(). Podrobne si ich preberieme až v kapitole venovanej vytváraniu funkcií.

Funkcia printf() má návratovú hodnotu typu int. Táto hodnota je rovná počtu znakov zapísaných do výstupu, alebo špeciálne hodnota EOF v prípade chyby zápisu. Hodnota EOF sa používa ako označenie konca súboru (End Of File), taktiež konca štandardného vstupu a výstupu. EOF je definovaný v súbore <stdio.h> a k jeho využitiu sa ešte vrátime.

Prvým argumentom je konštantný reťazec, ktorý funkcia printf() tlačí do stdout. Ten môže obsahovať špeciálne sekvencie, na ktorých miesto dosadzuje funkcie ďalšie argumenty (začínajú znakom %). Z toho vyplýva, že koľko je špeciálnych sekvencií v tomto reťazci, toľko ďalších argumentov oddelených čiarkou funkcii printf() musíme odovzdať. Argumenty by mali mať očakávaný dátový typ, ktorý je daný špeciálnou sekvenciou.

Formát tejto sekvencie je nasledujúci:

%[flags][width][.prec][h|l|L]type

Všetko, čo je v deklarácii v hranatých zátvorkách [] v deklarácii môže, ale nemusí byť. Znak | chápte ako “alebo”. Napríklad [h | l | L] znamená, že v deklarácii sekvencie môže byť h alebo l alebo L alebo ani jedna z týchto možností.

Ako vidíte, sekvencia začína znakom percento a končí znakom určujúci typ argumentu, ktorý sa bude tlačiť (a tiež ako (!) sa bude tlačiť). Napríklad už poznáme sekvenciu %i, ktorá tlačí celé číslo so znamienkom. Ak chcete vytlačiť priamo znak percento, potom ho stačí napísať dvakrát za sebou (%%). V nasledujúcej tabuľke je prehľad ďalších možných typov.

Prehľad typov špeciálne sekvencie reťazca vo funkcii printf ()

type význam
d, i Celé číslo so znamienkom (Tu nie je medzi d a i rozdiel. Rozdiel pozri scanf() nižšie).
u Celé číslo bez znamienka.
o Číslo v osmičkové sústave.
x, X Číslo v šestnástkovej sústave. Písmená ABCDEF sa budú tlačiť ako malé pri použití malého x, alebo veľké pri použití veľkého X.
p Ukazovateľ (pointer).
f Racionálne číslo (float, double) bez exponentu.
e, E Racionálne číslo s exponentom, implicitne jedna pozícia pred desatinnou bodkou a šesť za ňou. Exponent uvádza malé alebo veľké E.
g, G Racionálne číslo s exponentom alebo bez neho (podľa absolútnej hodnoty čísla). Neobsahuje desatinnú bodku, pokiaľ nemá desatinnú časť.
c Jeden znak.
s Reťazec.

Príklad:

print.c
/* print.c */
 
#include <stdio.h>
 
int main(void)
{
    const char *COPYRIGHT = "(C)";
    const int ROK = 2003;
 
    printf("%i %u %o %x %X %f %e %G\n", -5, -5, 200, 200, 200, 10.0,
           10.0, 10.0);
    printf("%s %i\n", COPYRIGHT, ROK);
    return 0;
}

Výstup bude nasledujúci:

	-5 4294967291 310 c8 C8 10.000000 1.000000e +01 10
	(C) 2003

Z čísla -5 sa stalo pri použití %u číslo 4294967291. To je dané interpretáciou bitového zápisu, kedy sa prvý bit zľava nepočítal ako identifikátor znamienka, ale ako súčasť čísla.

Položky width a .prec určujú dĺžku výstupu. Dĺžka width určuje minimálny počet znakov na výstupe. Môžu byť sprevádzané medzerami alebo nulami. .prec určuje maximálny počet znakov na výstupe pre reťazec. Pre celé čísla je to minimum zobrazených znakov, pre racionálny počet miest za desatinnou bodkou (má to teda viac významov v závislosti od typu).

Položka width môže nadobúdať nasledujúce hodnoty:

Hodnoty width
n Vytlačí sa aspoň n znakov doplnených medzerami
0n Vytlačí sa aspoň n znakov doplnených nulami
* Vytlačí sa aspoň n znakov, kde n je ďalší argument funkcie printf()

Položka .prec môže nadobúdať nasledujúcich hodnôt:

Hodnoty .prec
.0 Pre e, E, f nezobrazí desatinnú bodku
Pre d, i, o, u, x nastaví štandardné hodnoty
.n Pre d, i, o, u, x minimálny počet číslic
Pre e, E, f počet desatinných číslic
Pre g, G počet platných miest
Pre s maximálny počet znakov
.* Ako presnosť bude použitý nasledujúci parameter funkcie printf()

Príklad:

print2.c
/* print2.c */
 
#include <stdio.h>
 
int main(void)
{
    printf("%06i %06u %06x %6x %06f %06E %06G\n\n", -5, -5, 200, 200, 10.0,
           10.0, 10.0);
 
    printf("%*s %1s %6s %%06.2G\n", 10, "%*i", "%06.2f", "%06.0E");
 
    printf("%*i %06.2f %06.0E %06.2G\n", 10, -5, 10.0 / 3, 10.0 / 3,
           10.0 / 3);
    printf("\n%.8s %0*.*f\n", "Posledny vystup:", 10, 4, 10.0 / 3);
    return 0;
}

Výstup z programu:

-00005 4294967291 0000c8     c8 10.000000 1.000000E+01 000010

       %*i %06.2f %06.0E %06.2G
        -5 003.33 03E+00 0003.3


Posledny 00003.3333

Všimnite si použitie %% pri druhom volaní funkcie printf() (riadok 10) a tiež rozdielu mezdi %06.2f a %06.2G (riadok 12). Tiež si všimnite, že do dĺžky čísla sa započítavajú aj pozície pre znamienko. Čísla, ktoré sú dlhšie ako požadovaná minimálna dĺžka sa neskracujú, reťazce áno.

Príznak flags môže nadobúdať hodnoty z nasledujúcej tabuľky:

Hodnoty flags
- Výsledok je zarovnaný zľava
+ U čísla bude vždy zobrazené znamienko
medzera u kladných čísel bude namiesto znamienka “+” medzera
# Pre o, x, X výstup ako konštanty jazyka C
pre e, E, f, g, G vždy zobrazí desatinnú bodku
pre g, G ponechá nevýznamné nuly
pre c, d, i, s, u nemá význam.

Znaky h l a L označujú typ čísla. Znak h typ short (nemá zmysel pre 16-bitové prekladača), l dlhé celé číslo, L long double.

Príklad:

print3.c
/* print3.c */
 
#include <stdio.h>
 
int main(void)
{
    long int x;
    long double y = 25.0L;
    x = -25l;                      /* je mozeo pisat iba x = -25, pretoze
                                   prekladac vie, ze x je long a tak si
                                   cislo -25 prevedie na -25l;
                                   takto je to vsak "cistejsie" a citalnejsie */
 
    printf("%10s <%+5i> <% 5ld> <%x>\n", "Cisla:", 25, x, -25);
    printf("%-10s <%-+5i> <% 5Lf> <%#x>\n", "Cisla:", 25, y, -25);
    return 0;
}

Výstup z programu:

    Cisla: <+25> <-25> <ffffffe7>
Cisla: <+25> <25.000000> <0xffffffe7>

Funkcia scanf()

Funkcia scanf sa používa pre formátovaný štandardný vstup (z stdin), čo býva obvykle vstup z klávesnice. Deklarácia funkcie scanf() vyzerá takto:

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

Návratová hodnota je rovná počtu bezchybne načítaných a do pamäti uložených položiek, alebo hodnota EOF (End Of File) pri pokuse čítať položky z uzavretého vstupu. Prvý argument je rovnako ako u funkcie printf() reťazec, ktorý obsahuje špeciálne sekvencie určujúce, čo sa má načítať. Formát takejto sekvencie je nasledujúci:

% [*] [Width] [h | l | L] type

Význam položiek je nasledovné:

* Preskočí popísaný vstup (načíta, ale nikam nezapisuje)
width maximálny počet vstupných znakov
h | l | L modifikácie typu (ako u printf())
type typ konverzie

Ako vidíte, sekvencia vždy začína znakom percento a končí typom konverzie. Možné typy konverzie sú v nasledujúcej tabuľke:

význam type
d celé číslo
u celé číslo bez znamienka
o osmičkové celé číslo
x šestnástkovej celé číslo
i celé číslo, zápis zodpovedá zápisu konštanty v jazyku C, napr 0x Uvádza číslo v šestnástkovej sústave
n počet doteraz prečítaných znakov v aktuálnom volaní funkcie scanf()
e, f, g racionálne čísla typu float, možno ich modifikovať pomocou l a L
s reťazec; úvodné oddeľovače sú preskočené, na konci je pridaný znak '\0'
c vstup znaku; ak je určená šírka, je čítaný reťazec bez preskočenia oddeľovačov
[search_set] ako s, ale so špecifikáciou vstupnej množiny znakov, je možný aj interval, napríklad %[0-9], aj negácia, napríklad %[^a-c].

Oddeľovače sú tzv. biele znaky (tabulátor, medzera, koniec riadku (ENTER)). Tie sa pri čítaní zo vstupu preskakujú (výnimkou môže byť typ c). Načítanie teda prebieha tak, že sa najskôr preskočia oddeľovače a potom sa načíta požadovaný typ. Ak je požadovaný typ napríklad číslo, ale mi namiesto toho na vstupe máme písmeno, potom dôjde k chybe. Ak načítame požadovaný typ, uloží sa na adresu, ktorá je uložená v ďalšom argumente funkcie scanf(). Volanie funkcie scanf() môže vyzerať napríklad takto:

scanf("%i",&x);

Takto sa pokúsi funkcia scanf() načítať číslo a uložiť ho do premennej x, ktorej adresa je ďalším argumentom funkcie scanf(). Znovu zdôrazňujem, že je to adresa miesta v pamäti, kde je premenná x', do ktorej sa načítaná hodnota uloží. Adresa premennej sa získava pomocou operátora & a môže byť uložená v ukazovateli.

Ak sa to podarí, vráti funkcia číslo 1 (načítaná jedna správna položka). Ak sa to nepodarí (napr. namiesto čísla zadáme nejaké znaky, alebo vstup ukončíme), vráti sa EOF. Návratovú hodnotu sa naučíme využívať až v kapitolách venovaných riadenia programu.

Príklad:

scan1.c
/* scan1.c */
 
#include <stdio.h>
 
int main(void)
{
    int x = -1;
 
    printf("Zadaj cislo ako konstantu jazyka C\n"
           "napr. 10 0x0a alebo 012: ");
    scanf("%i", &x);
    printf("Zadal si cislo %i\n", x);
    return 0;
}

Keď program preložíte a spustíte, bude funkcia scanf() čakať na vstup (z klávesnice), kým nejaký nedostane, alebo kým nebude vstup uzavretý. Vstup z klávesnice sa programu odošle až po stlačení klávesy ENTER.

Možné výstupy:

Zadaj cislo ako konstantu jazyka C
napr. 10 nebo 0x0a nebo 012: -50
Zadal si cislo -50
Zadaj cislo ako konstantu jazyka C
napr. 10 nebo 0x0a nebo 012: 0xff
Zadal si cislo 255
Zadaj cislo ako konstantu jazyka C
napr. 10 alebo 0x0a nebo 012: ff
Zadal si cislo -1

Teraz už viete vytvárať skutočne interaktívne programy (-:

Pri poslednom spustení programu som zadal “ff”, čo nie je číslo ale textový reťazec, preto funkcia scanf() do premennej x nič neuložila a tak v nej zostalo číslo -1.

Vstupný prúd (stdin) môžete prerušiť vo Windows a v DOSe klávesovou skratkou CTRL + Z (stlačte a držte CTRL ak tomu stlačte písmeno “z”). V Linuxe pomocou klávesovej skratky CTRL + D. Vyskúšajte.

Ukážem ešte jeden príklad a ďalšie si vymyslite sami. Kombinácií sekvencií môže byť ako u funkcie printf() tak scanf() veľa, takže sa s nimi vyhrajte a sledujte ako pracujú. Zatiaľ sa nepokúšajte načítať reťazce, najskôr si budeme musieť vysvetliť prácu s poľami.

Reťazec zo štandardného vstupu je ukončený oddeľovačom (medzera, ENTER, …).

scan2.c
/* scan2.c*/
 
#include <stdio.h>
 
int main(void)
{
    int x = 0;
    double f = 0.0;
    char znak = 'a';
 
    printf("Zadaj jeden znak a cislo max. 2 znaky dlhe: ");
    scanf("%c %2d", &znak, &x);
    printf("\t--- zadal si %c a %i\n", znak, x);
 
    printf("Zadaj retazec a potom racionalne cislo: ");
    scanf("%*s %lf", &f);
    printf("\t--- zadal si %f\n", f);
 
    return 0;
}

Možné výstupy z programu:

Zadaj jeden znak a cislo max 2 znaky dlhe: B 55
        --- Zadal si B a 55
Zadaj retazec a potom racionálne cislo: ahoj 15.3
        --- Zadal si 15.300000
Zadaj jeden znak a cislo max 2 znaky dlhe: B 55 ahoj 15.3 nazdar
        --- Zadal si B a 55
Zadaj retazec a potom racionalne cislo: --- zadal si 15.300000

Všimnite si, ako prebieha načítanie zo štandardného vstupu. V prvom príklade som po spustení programu napísal “B 55” a stlačil ENTER. Tým som odovzdal nejaký vstup a prvé volanie funkcie scanf() si najskôr načítalo požadovaný znak a potom číslo. Potom prebehla funkcia printf() a spustilo sa druhé volanie scanf(). Tú sa zastavila a čakala na ďalší vstup. Napísal som teda reťazec “ahoj” a za ním racionálne číslo a poslal tento vstup stlačením ENTER. Funkcia scanf() reťazec preskočila (%*s) a načítala číslo za ním.

Medzery, ktoré sú vo formátovacom reťazci funkcie scanf(), nehrajú žiadnu úlohu a nemusia tam byť.

Pri druhom spustení programu som poslal na vstup rovno znak, číslo, reťazec, ďalšie číslo a ďalší reťazec. Program pri prvom volaní scanf() načítal B a 55, spustil printf() a ďalšie volanie scanf() pokračovalo v čítaní vstupu tam, kde predchádzajúce scanf() skončilo. Preskočilo reťazec “ahoj” a načítalo číslo 15.3.

Mohli by ste tiež zadať znak, stlačením ENTER výstup poslať programu a potom číslo a zase ENTER, reťazec a ENTER … atď. Skúšajte si a sledujte ako sa program chová.

Všimnite si, že keď zadáte číslo, ktoré má byť dlhé len 2 znaky, potom každý ďalší znak už druhé volanie funkcie scanf() vyhodnotí ako reťazec. Tiež si uvedomte, že číslo môže byť chápané aj ako reťazec alebo znak. Ide len o to, ako bude do pamäti ukladané. Ak načítate číslo 5, bude uložené ako číslo 5, ak načítate znak 5 bude uložené číslo z ASCII tabuľky zodpovedajúce znaku 5 (čo je číslo 53).

Ak sa v prvom argumente funkcie scanf() vyskytne nejaký reťazec, znamená to, že funkcia scanf() má práve tento reťazec načítať (a nikam neuložiť). Ak však taký reťazec na vstupe nie je, dôjde k chybe.

scan3.c
/* scan3.c */
 
#include <stdio.h>
 
int main(void)
{
    int zamok = 5;
    int navrat;
 
    printf("Hodnota zamku je teraz %+5i\n", zamok);
    printf("Pokial chcete hodnotu zmenit, zadajte heslo a novou hodnotu\n"
           "a stlacte ENTER. Napriklad: heslo 25\n>> ");
 
    /* heslo je 1234567890 */
    navrat = scanf("1234567890 %i", &zamok);
 
    printf("Bolo nacitanych %i spravnych poloziek\n", navrat);
    printf("Hodnota zamku je teraz %+5i\n", zamok);
    return 0;
}

Výstup z programu:

Hodnota zamku je teraz +5
Ak chcete hodnotu zmenit, zadajte heslo a novú hodnotu
a stlacte ENTER. Napriklad: heslo 25
>> 1234567890 -2345
Bolo nacitanych 1 spravnych poloziek
Hodnota zamku je teraz -2345

Všimnite si ako bola vložená návratová hodnota funkcie scanf() do premennej navrat.

kruzok/jazyk_c/standardny_vstup_a_vystup.txt · Posledná úprava: 2024/02/16 17:38 od 127.0.0.1