Prednáška 10
- Oznamy
- Textové súbory
- Čítanie a zapisovanie po znakoch
- Čítanie a zapisovanie po riadkoch
- Testovanie konca súboru
- Prístupy k spracovaniu textového vstupu
- Kontrola správnosti vstupu vo
scanfafscanf - Spracovanie vstupu pozostávajúceho z postupnosti čísel
- Zhrnutie práce so súbormi
- Príklad kombinácie čítania po znakoch a po číslach
- Jednoduché šifrovanie: Caesarova šifra
- Ďalšie detaily funkcií
printfafprintf - Ďalšie detaily funkcií
scanfafscanf
Oznamy
Budúci týždeň veľa dôležitých vecí:
- Pondelok prednáška: úvod do rekurzie, dôležité učivo
- Pondelok cvičenie: práca na počítači, rekurzia, súbory
- Pondelok 22:00: termín miniprojektu
- Utorok cvičenie: papierová rozcvička na rekurziu, potom práca na počítači
- Streda prednáška: pokračujeme v rekurzii
- Streda 18:10: semestrálny test (vrátane úvodu do rekurzie), viď Test 1
Odporúčame teda začať sa pripravovať na test a odovzdať miniprojekt už tento týždeň.
Textové súbory
Na načítavanie a vypisovanie dát sme doposiaľ používali výhradne konzolu. V praxi však často potrebujeme spracovávať dáta uložené v súboroch.
Zameriame sa na súbory v textovom formáte, s ktorými sa pracuje podobne ako s konzolou.
Základy: typ FILE * a funkcie fopen, fclose, fprintf, fscanf
So súbormi sa pri použití knižnice stdio.h pracuje pomocou typu FILE
* (veľkými písmenami).
- Hviezdička (
*) znamená, že ide o smerník, čo bude vysvetlené o pár týždňov. - Je to smerník na štruktúru typu
FILE, ktorá obsahuje informácie o súbore, s ktorým sa práve pracuje. - Premenné pre prácu so súbormi tak možno definovať napríklad takto:
FILE *fin, *fout;
Otvorenie súboru pre čítanie
fin = fopen("vstup.txt", "r");- Otvorí súbor s názvom
vstup.txt(prípadne možno zadať kompletnú cestu k súboru). - Ak taký súbor neexistuje alebo sa nedá otvoriť, do
finpriradí špeciálna hodnotaNULL. - Z otvoreného súboru môžeme čítať napríklad pomocou
fscanf, ktorá je analógiou kscanf. - Napríklad
fscanf(fin, "%d", &x);
Otvorenie súboru pre zápis
fout = fopen("vystup.txt", "w");- Vytvorí súbor s názvom
vystup.txt. Ak už existoval, zmaže jeho obsah (keby sme vo volanífopennamiesto"w"použili"a", pridávalo by sa na koniec existujúceho súboru). - Ak sa nepodarí súbor otvoriť, do
foutpriradíNULL. - Do otvoreného súboru môžeme zapisovať napr. pomocou funkcie
fprintf, ktorá je analógiou kprintf. - Napr.
fprintf(fout, "%d", x);
Zatvorenie súboru
- Po ukončení práce so súborom je ho potrebné zavrieť pomocou
fclose(f); - Počet súčasne otvorených súborov je obmedzený.
Príklad
Nasledujúci program načíta číslo n a následne n celých čísel zo
súboru vstup.txt. Do súboru vystup.txt vypíše vstupné čísla v
opačnom poradí.
#include <stdio.h>
#include <assert.h>
#define NMAX 100
int main(void) {
// otvorime vstupny subor a skontrolujeme, ze sa podarilo
FILE *fin = fopen("vstup.txt", "r");
assert(fin != NULL);
// nacitame pocet cisel
int n;
fscanf(fin, "%d", &n);
assert(n >= 0 && n <= NMAX);
// vytvorime pole a nacitame cisla
int a[NMAX];
for (int i = 0; i < n; i++) {
fscanf(fin, "%d", &a[i]);
}
fclose(fin);
// otvorime vystupny subor a skontrolujeme, ze sa podarilo
FILE *fout = fopen("vystup.txt", "w");
assert(fout != NULL);
// vypiseme cisla odzadu
for (int i = n-1; i >= 0; i--) {
fprintf(fout, "%d ", a[i]);
}
fclose(fout);
}
Štandardný vstup a výstup ako súbor
So štandardným vstupom a výstupom sa pracuje rovnako ako so súborom. V
stdio.h sú definované dve konštanty typu FILE * pre štandardný vstup a výstup.
FILE *stdin, *stdout;
Tie môžu byť použité tam, kde sa očakáva súbor. Napríklad volanie
fscanf(stdin, "%d", &x) je ekvivalentné volaniu scanf("%d", &x).
Ten istý kód sa tak dá použiť na prácu so súbormi aj so štandardným
vstupom resp. výstupom – stačí len podľa potreby nastaviť premennú typu
FILE *. Typické použitie je napríklad nasledovné:
#include <stdio.h>
#include <string.h>
int main(void) {
// do retazca str nacitame z konzoly meno suboru alebo pomlcku
char str[101];
scanf("%100s", str);
// do fout otvorime subor alebo pouzijeme konzolu
FILE *fout;
if (strcmp(str, "-") == 0) {
fout = stdout;
} else {
fout = fopen(str, "w");
}
// zapiseme nieco do fout
fprintf(fout, "Hello world!\n");
// ak treba, zatvorime subor
if (strcmp(str, "-") != 0) {
fclose(fout);
}
}
Čítanie a zapisovanie po znakoch
Knižnica stdio.h obsahuje aj funkcie na čítanie a zapisovanie súboru po
znakoch.
- Funkcia
int getc(FILE *f)načíta a vráti jeden znak zo súboru- Ak načítanie neprebehne úspešne, výsledkom je špeciálna
konštanta
EOF, ktorá je vždy rôzna od ľubovoľnej hodnoty typuchar - Neukladajte výstupnú hodnotu funkcie
getcdo premennej typuchar, lebo nebudete vedieť rozoznať koniec súboru.
- Ak načítanie neprebehne úspešne, výsledkom je špeciálna
konštanta
- Funkcia
int putc(int c, FILE *f)zapíše znakcdo súboru.
Príklad: kopírovanie súboru
Nasledujúci program skopíruje obsah súboru original.txt do súboru
kopia.txt.
#include <stdio.h>
int main(void) {
FILE *fin = fopen("original.txt", "r");
FILE *fout = fopen("kopia.txt", "w");
int c = getc(fin);
while (c != EOF) {
putc(c, fout);
c = getc(fin);
}
fclose(fin);
fclose(fout);
}
Cvičenie: čo robí nasledujúci program?
- výsledkom priradenia
c = getc(fin)je hodnota priradená do premennejc
#include <stdio.h>
int main(void) {
FILE *fin = fopen("vstup.txt", "r");
int c;
while ((c = getc(fin)) != '\n') {
putc(c, stdout);
}
putc(c, stdout);
fclose(fin);
}
Funkcia ungetc
- Často sa stáva, že pri načítavaní znakov nájdeme koniec práve načítavaného úseku až po načítaní znaku, ktorý už nie je žiadúce prečítať.
- Vtedy je užitočné posunúť sa v načítavaní o jeden krok nazad.
- Túto úlohu realizuje funkcia
int ungetc(int c, FILE * f). - Väčšinou ako
cpoužijeme posledne načítaný znak zo súboruf. - Môžeme však použiť aj iný znak, ktorý bude virtuálne pridaný na
začiatok neprečítanej časti súboru. Súbor sa reálne nemení, ale pri
nasledujúcom čítaní z neho sa ako prvý prečíta znak
c. - V prípade úspechu
ungetc(c,f)vráti hodnotuc; v prípade neúspechu je výstupomEOF. - Takéto správanie funkcie
ungetcje však garantované len ak sa táto funkcia nevolá viackrát za sebou.
Príklad č. 1: Nasledujúci kus programu skonvertuje reťazec
pozostávajúci zo znakov '0' až '9' na zodpovedajúcu číselnú
hodnotu. Keď narazí na prvý znak, ktorý nie je cifra, vráti ho, aby sa
dal použiť pri ďalšom spracovávaní.
int hodnota = 0;
int c = getc(stdin);
while (c >= '0' && c <= '9') {
hodnota = hodnota * 10 + (c - '0');
c = getc(stdin);
}
ungetc(c, stdin);
Príklad č. 2: Nasledujúci program prečíta číslo pomocou funkcie
fscanf, predtým však musí prečítať neznámy počet znakov '$'.
#include <stdio.h>
int main(void) {
FILE *fin = fopen("vstup.txt", "r");
int c = getc(fin);
while (c == '$') {
c = getc(fin);
}
ungetc(c, fin);
int hodnota;
fscanf(fin, "%d", &hodnota);
printf("%d\n", hodnota);
fclose(fin);
}
Čítanie a zapisovanie po riadkoch
Pripomeňme si funkcie fgets a fputs, ktoré umožňujú načítavať a zapisovať riadky zo súboru.
Funkcia fgets(str, n, f) načíta riadok zo súboru f do poľa znakov str dĺžky n.
- Súbor
fje typuFILE *. - Funkcia
fgetsnačítava znaky dovtedy, kým narazí na koniec riadku (\n) alebo koniec súboru alebo kým zo súboru neprečítan-1znakov. - Prípadný znak
\nna konci riadku sa nezahodí, ale pridá sa na koniec reťazcastr(pokiaľ nebolo načítaných príliš veľa znakov). - Reťazec
strvždy ukončí nulovým znakom0. - Výstupom funkcie je v prípade načítania aspoň jedného znaku načítaný
reťazec
str; inak je výstupomNULLa reťazecstrostáva nezmenený.
Funkcia fputs(str, f) vypíše ľubovoľný reťazec str do súboru f.
strmôže obsahovať hocikoľko koncov riadkov (aj nula)- pri chybe vráti
EOF, inak vráti nezáporné celé číslo
Príklad: nasledujúci program načítava riadky v súbore vstup.txt a
vypíše ich na konzolu, pričom na začiatok každého riadku pridá jeho
poradové číslo. Funguje správne za predpokladu, že žiaden z riadkov nie
je dlhší ako 100 znakov vrátane znaku '\n' na konci riadku.
#include <stdio.h>
#define NMAX 101
int main(void) {
char str[NMAX];
int num = 0;
FILE *fin = fopen("vstup.txt", "r");
while (fgets(str, NMAX, fin) != NULL) {
printf("%d: ", num); // výpis čísla riadku
fputs(str, stdout); // výpis samotného riadku
num++;
}
fclose(fin);
}
Cvičenie:
- Zistite, ako sa správa uvedený program, keď posledným znakom v
súbore je resp. nie je znak
'\n'. - Zistite, čo program vypíše na výstup pre súbor, ktorý obsahuje jediný riadok o 200 znakoch.
Testovanie konca súboru
Funkcia feof(subor) vráti true práve vtedy, keď niektorá funkcia
čítajúca zo súboru subor už niekedy pokúšala čítať za koncom súboru.
- Použitie uvidíme nižšie.
Prístupy k spracovaniu textového vstupu
Časté schémy spracovania textového vstupu zo súboru alebo konzoly:
- Pomocou
fscanfnačítavame jednotlivé čísla, slová a pod. (vhodné, keď všetky biele znaky považujeme za ekvivalentné oddeľovače) - Čítame po znakoch pomocou
getc(pomerne univerzálne, ale niekedy prácne) - Čítame po riadkoch pomocou
fgetsdo reťazca, potom reťazec spracovávame (vhodné, keď riadok je ucelená časť súboru, ktorá nás zaujíma)
Niekedy je užitočné tieto prístupy aj kombinovať, viď príklad nižšie.
Príklad: chceme nájsť dĺžku najdlhšieho riadku v súbore
- Prvá možnosť je čítanie riadkov do reťazca a ich spracovanie (problém, ak je riadok príliš dlhý)
- Druhá možnosť je čítať súbor po znakoch, pričom si potrebujeme udržiavať “stav”: koľko písmen sme už videli v aktuálnom riadku
#include <stdio.h>
#include <string.h>
#define NMAX 100
int main(void) {
FILE *fin = fopen("vstup.txt", "r");
int maxDlzka = 0;
char str[NMAX];
while (fgets(str, NMAX, fin) != NULL) {
int dlzka = strlen(str);
if (dlzka > maxDlzka) {
maxDlzka = dlzka;
}
}
fclose(fin);
printf("Najdlhsi riadok ma dlzku %d\n", maxDlzka);
}
#include <stdio.h>
int main(void) {
FILE *fin = fopen("vstup.txt", "r");
int maxDlzka = 0;
int dlzka = 0;
int c = getc(fin);
while (c != EOF) {
dlzka++;
if (c == '\n') {
if (dlzka > maxDlzka) {
maxDlzka = dlzka;
}
dlzka = 0;
}
c = getc(fin);
}
if (dlzka > maxDlzka) { // Posledny riadok nemusi koncit symbolom \n.
maxDlzka = dlzka;
}
fclose(fin);
printf("Najdlhsi riadok ma dlzku %d\n", maxDlzka);
}
Cvičenie: čo ak chceme zistiť, koľký riadok v súbore bol ten najdlhší?
Kontrola správnosti vstupu vo scanf a fscanf
Funkcie scanf a fscanf vracajú počet úspešne načítaných hodnôt zo vstupu.
- V prípade chyby hneď na začiatku vstupu tak napríklad vráti
0. - Ak hneď na začiatku narazí na koniec vstupu, vráti hodnotu
EOF - Vstup z konzoly sa dá ukončiť pod Linuxom ako
Ctrl+Dresp. pod Windowsom akoCtrl+ZaEnter
Príklad: zadávanie dátumu vo formáte deň.mesiac.rok s kontrolou
vstupu:
#include <stdio.h>
void vypisDatum(int d, int m, int r) {
printf("%02d.%02d.%04d\n", d, m, r);
}
int main(void) {
int d, m, r;
printf("Zadaj datum ako den.mesiac.rok: ");
int uspech = scanf("%d.%d.%d", &d, &m, &r);
if (uspech == 3) {
printf("Datum je ");
vypisDatum(d, m, r);
} else {
printf("Nebol zadany korektny datum.\n");
printf("Pocet uspesne nacitanych poloziek: %d.\n", uspech);
}
}
- V tomto programe sme vo funkcii
scanfpoužili špecifikáciu konverzie%d.%d.%d, ktorá očakáva tri celé čísla oddelené bodkami. Potom kontrolujeme, či sa podarilo načítať všetky tri čísla. - Vo funkcii
printfsme použili špecifikáciu konverzie%02da%04d, ktorá zabezpečí, že čísla budú vypísané s minimálnou dĺžkou 2 resp. 4 znaky, pričom sa doplnia nuly zľava. - Viac o funkciách
scanfaprintfsi môžete prečítať na konci týchto poznámok.
Ďalší program počíta súčet postupne zadávaných čísel, až kým je zadané nekorektné číslo alebo koniec súboru:
#include <stdio.h>
int main(void) {
double sum = 0;
double x;
while (scanf("%lf", &x) == 1) {
sum += x;
}
printf("Sucet je %.2f\n", sum);
}
Spracovanie vstupu pozostávajúceho z postupnosti čísel
Často na vstupe očakávame postupnosť číselných hodnôt oddelených bielymi
znakmi. Pozrime sa na tri obvyklé možnosti, ako môže byť takýto vstup
zadaný a spracovaný pomocou funkcie fscanf.
Formát 1: N (počet čísel) a následne N ďalších čísel. Pre jednoduchosť často používame na tomto predmete, ale je menej častý v praxi.
#include <stdio.h>
#include <assert.h>
#define NMAX 100
int main(void) {
FILE *f;
int a[NMAX], n, uspech;
f = fopen("vstup.txt", "r");
assert(f != NULL);
uspech = fscanf(f, "%d ", &n);
assert(uspech == 1 && n >= 0 && n <= NMAX);
for (int i = 0; i < n; i++) {
uspech = fscanf(f, "%d ", &a[i]);
assert(uspech == 1);
}
fclose(f);
// tu pride spracovanie dat v poli a
}
Formát 2: postupnosť čísel ukončená číslom -1 alebo inou špeciálnou hodnotu.
// otvorime subor f ako vyssie
n = 0;
int x;
uspech = fscanf(f, "%d ", &x);
assert(uspech == 1);
while (x != -1) {
assert(n < NMAX);
a[n] = x;
n++;
uspech = fscanf(f, "%d ", &x);
assert(uspech == 1);
}
// zatvorime subor a spracujeme data
Formát 3: čísla, až kým neskončí súbor (najtypickejší prípad v praxi).
Priamočiary prístup nefunguje vždy správne:
// otvorime subor f ako vyssie
n = 0;
while (!feof(f)) {
assert(n < NMAX);
uspech = fscanf(f, "%d", &a[n]);
assert(uspech == 1); // bude tu padat
n++;
}
// zatvorime subor a spracujeme data
- Tento program nefunguje, ak po poslednom čísle v súbore nasleduje ešte koniec riadku (čo je obvyklé).
- Pri načítaní tohto čísla skončíme na symbole konca riadku,
fscanfsa teda nepokúsi čítať za koncom súboru. - Spustí sa teda ďalšie opakovanie cyklu, v ktorom sa už ale nepodarí
ďalšie číslo načítať a
assertukončí program s chybovou hláškou. - Aj bez príkazu
assertby sme mali problém, že do poľa by sa uložila nezmyselná hodnota.
Tento nedostatok môžeme napraviť napríklad tak, že vo volaní funkcie
fscanf dáme vo formátovacom reťazci za %d medzeru. Tá sa bude
pokúšať preskočiť všetky biele znaky až po najbližší nebiely; pritom
natrafí na koniec súboru a feof(f) už bude vracať true.
#include <stdio.h>
#include <assert.h>
#define NMAX 100
int main(void) {
FILE *f;
int a[NMAX], n, uspech;
f = fopen("vstup.txt", "r");
assert(f != NULL);
n = 0;
while (!feof(f)) {
assert(n < NMAX);
uspech = fscanf(f, "%d ", &a[n]); // medzera za %d
assert(uspech == 1);
n++;
}
fclose(f);
// tu pride spracovanie dat v poli a
}
Cvičenie: upravte úryvky programu vyššie tak, aby pracoval s číslami
typu double.
Zhrnutie práce so súbormi
- Ukázali sme si prácu so súbormi pomocou knižnice
stdio.h - Súbor je reprezentovaný typom
FILE *. - Na otváranie a zatváranie súborov slúžia funkcie
fopen,fclose. - Na vypísanie kombinácie čísel a textov slúži funkcia
fprintf, čísla a slová načítavame pomocoufscanf. - Po znakoch pracujeme funkciami
putcagetc, niekedy sa zíde ajungetc. - Po riadkoch pracujeme pomocou
fgetsafputs. - V programoch, ktoré sa majú reálne používať, je vhodné kontrolovať, či tieto operácie úspešne zbehli. Tiež pozor na to, aby pri načítaní reťazcov nenastalo pretečenie poľa.
- K niektorým funkciám existujú aj verzie pre konzolu, napr.
printfascanf. - S konzolou tiež môžeme pracovať pomocou premenných
stdin,stdout.
Ďalšie možnosti
- Okrem textových súborov existujú aj binárne, kde nie je číslo
zapísané ako postupnosť cifier, ale priamo ako bajty.
- Binárne súbory majú často menšiu veľkosť a rýchlejšie sa s nimi pracuje, ťažšie však ručne skontrolujete, čo v súbore je.
- V knižnici
stdio.hexistujú na prácu s binárnymi súbormi funkciefwriteafread.
- V mnohých programovacích jazykoch existujú knižnice na načítavanie a zapisovanie často používaných formátov súborov.
Príklad kombinácie čítania po znakoch a po číslach
Príklad: Máme súbor s číslami oddelenými bielymi znakmi (medzery, tabulátory, konce riadkov,…), pričom medzi dvoma číslami môže byť aj viac ako jeden oddeľovač. Chceme spočítať súčet čísel na každom riadku.
- Nepríjemná kombinácia rozlišovania koncov riadku od iných bielych znakov a čítania formátovaných hodnôt (čísel)
- Môžeme prečítať riadok do reťazca a rozložiť na čísla (napr.
funkciou
sscanfa so špecifikáciou konverzie%n, ktorá uloží počet načítaných znakov). - Program nižšie však používa kombináciu
getc,ungetcafscanf- Biele znaky spracúva pomocou funkcie
getc. Ak je niektorý z týchto znakov koncom riadku, vypíše zistený súčet čísel. - Po nájdení nebieleho znaku ho vráti do vstupného prúdu funkciou
ungetc, prečíta číslo pomocou funkciefscanfa aktualizuje súčet. - Na zistenie, či je prečítaný znak biely, využíva program funkciu
isspacez knižnicectype.h. - Program predpokladá, že aj posledný riadok vstupného súboru je
ukončený symbolom
\n.
- Biele znaky spracúva pomocou funkcie
#include <stdio.h>
#include <ctype.h>
int main(void) {
FILE *fin = fopen("vstup.txt", "r");
int sucet = 0;
int hodnota;
while (!feof(fin)) {
int c = getc(fin);
// Precitaj biele znaky po najblizsi nebiely
while (c != EOF && isspace(c)) {
if (c == '\n') { // Na konci riadku vypis sucet
printf("Sucet %d\n", sucet);
sucet = 0;
}
c = getc(fin);
}
if (c == EOF) { // Koniec suboru
break;
}
ungetc(c, fin);
fscanf(fin, "%d", &hodnota);
sucet += hodnota;
}
fclose(fin);
}
Cvičenia:
- Čo program spraví, ak vstup obsahuje aj prázdne riadky?
- Upravte program, aby pracoval správne aj v prípade, že posledný
riadok nie je ukončený symbolom
\n.- Čo vlastne chceme považovať za posledný riadok?
- Upravte program, aby na výstupe vypisoval aj čísla na riadkoch oddelené medzerami.
Jednoduché šifrovanie: Caesarova šifra
Prácu so súbormi si teraz precvičíme na jednoduchej šifre. Caesarova šifra je šifra, pri ktorej sa každé písmeno vstupného reťazca posunie cyklicky o K miest v abecednom poradí, kde K je zadaný parameter šifry.
- Napríklad pre K=2 sa písmeno
Azmení naC, písmenobsa zmení nada písmenoZsa zmení naB. - Ukážeme si jej použitie pre anglickú abecedu (t. j. znaky
'a'až'z'a'A'až'Z'bez diakritiky); je ju ale možné upraviť napríklad aj tak, aby pracovala s ASCII kódmi.
Napríklad pre K=3:
Pôvodný text: HelloWorld
Šifrovaný text: KhoorZruog
Nasledujúci program zašifruje súbor.
#include <stdio.h>
#include <assert.h>
void encryptCaesar(FILE *fin, FILE *fout, int shift) {
assert(shift >= 0 && shift <= 25);
int c;
while ((c = getc(fin)) != EOF) {
if ((c >= 'A') && (c <= 'Z')) {
c = c + shift;
if (c > 'Z') {
c -= 26;
}
} else if ((c >= 'a') && (c <= 'z')) {
c = c + shift;
if (c > 'z') {
c -= 26;
}
}
putc(c, fout);
}
}
int main(void) {
int shift;
scanf("%d", &shift); // nacitame parameter o kolko posuvat pismena
FILE *fin = fopen("plaintext.txt", "r");
FILE *fout = fopen("ciphertext.txt", "w");
encryptCaesar(fin, fout, shift);
fclose(fin);
fclose(fout);
}
- Čo program spraví so znakmi, ktoré nie sú písmená?
- Dešifrovanie by fungovalo podobne, akurát by sa posun odpočítaval a ako špeciálny prípad by sa riešilo, keď nový znak má kód menší ako ‘a’. Môžeme ale tiež použiť program na šifrovanie a posun 26-K, kde K je posun použitý na šifrovanie.
- Caesarova šifra sa dá ľahko prelomiť.
- O šifrách, ktoré sa v súčasnosti používajú, sa môžete dozvedieť vo vyšších ročníkoch na predmete Kryptológia.
Ďalšie detaily funkcií printf a fprintf
Veľkou výhodou týchto funkcií je, že vieme v jednom volaní vypísať niekoľko hodnôt rôznych typov, pričom formát výpisu je možné rôzne nastaviť.
Vo všeobecnosti vyzerá volanie týchto funkcií nasledovne:
printf(format, hodnota1, hodnota2, ...);
fprintf(subor, format, hodnota1, hodnota2, ...);
- Prvý argument
printfje formátovací reťazec. - Bežné znaky z formátovacieho reťazca sa priamo vypíšu na výstup.
- Symbol
%začína takzvanú špecifikáciu konverzie a má za následok vypísanie ďalšieho argumentu funkcie (prvého, ktorý sa nepoužil). - V jednoduchých príkladoch za znakom
%nasleduje znak reprezentujúci typ vypísanej hodnoty. -
Pozor, typy jednotlivých argumentov musia byť v súlade s formátovacím reťazcom.
%d: celé číslo (typint).%ld: dlhé celé číslo (typlong int).%f: reálne číslo (typdouble).%e: reálne číslo vo vedeckej notácii, napr.5.4e7(typdouble).%c: znak (typchar).%s: reťazec (typchar *).%%: vypíše samotný znak%.
Formátovanie výstupu
- Formát vypísania daného argumentu možno upraviť nepovinnými
parametrami medzi symbolom
%a znakom konverzie. - Napríklad:
%.2f: vypíše reálne číslo na 2 desatinné miesta,%4d: ak má celé číslo menej ako 4 cifry, doplní vľavo medzery,%04d: podobne, ale dopĺňa nuly.
Nasledujúci program vypíše zadaný dátum vo formáte typu 02.01.2027:
#include <stdio.h>
void vypisDatum(int d, int m, int r) {
printf("%02d.%02d.%04d\n", d, m, r);
}
int main(void) {
vypisDatum(2, 1, 2027);
}
Celá špecifikácia konverzie pozostáva z nasledujúcich častí:
- Z povinného úvodného znaku
%. - Z nepovinných príznakov, napríklad
-, ktorého použitie vyústi v zarovnanie vypisovaného textu vľavo (bez jeho použitia sa text zarovná vpravo). Ďalšími príznakmi sú napríklad0(dopĺňanie núl naľavo),+(vypíše znamienko + pri kladných číslach), atď. - Z nepovinného celého čísla udávajúceho minimálnu šírku výpisu (minimálny počet „políčok”, do ktorých sa text vypíše).
- Z nepovinnej bodky nasledovanej celým číslom udávajúcim presnosť výpisu (pri reálnych číslach napríklad počet desatinných miest; presnosť má však svoju interpretáciu aj pri iných typoch dát).
- Z nepovinného modifikátora
l,ll, alebohprelong,long long, resp.short. - Z povinného symbolu konverzie (napr.
d,f,s, …).
Ďalšie detaily funkcií scanf a fscanf
Tieto funkcie vedia načítať viacero hodnôt naraz. Ich použitie:
scanf(format, &premenna1, &premenna2, ...)
fscanf(subor, format, &premenna1, &premenna2, ...)
Pomocou scanf možno načítať aj viacero premenných naraz:
#include <stdio.h>
void vypisDatum(int d, int m, int r) {
printf("%02d.%02d.%04d\n", d, m, r);
}
int main(void) {
int d, m, r;
printf("Zadaj den, mesiac a rok: ");
scanf("%d %d %d", &d, &m, &r);
vypisDatum(d, m, r);
}
Formátovací reťazec sa v scanf interpretuje nasledovne:
- Špecifikácia typu načítavaných premenných je podobná ako pri funkcii
printf.%dnačítaint%lfnačítadouble(pozor, tu je rozdiel odprintf, kde sa double vypisuje pomocou%f)%snačíta reťazec, konkrétne jedno slovo, t.j. postupnosť nebielych znakov. Ako argument sa zadá dostatočne veľké pole prvkov typuchar.%100snačíta slovo, ale najviac 100 znakov. Pole má mať veľkosť aspoň 101.%cnačíta jeden znak. Na rozdiel od všetkých predchádzajúcich typov, tu sa nepreskakujú biele znaky pred prvým nebielym.
- Biele znaky (angl. whitespace, t.j. medzery, konce riadkov,
tabulátory) vo formátovacom reťazci spôsobia, že funkcia
scanfčíta a ignoruje všetky biele znaky pred ďalším nebielym znakom. Jeden biely znak vo formátovacom reťazci tak umožní ľubovoľný počet bielych znakov na vstupe. - Ostatné znaky formátovacieho reťazca musia presne zodpovedať vstupu.
Nasledujúci príkaz tak napríklad načíta dátum vo formáte
deň.mesiac.rok:
scanf("%d.%d.%d", &d, &m, &r);