Programovanie (1) v C/C++
1-INF-127, ZS 2024/25
Prednáška 15: Rozdiel medzi revíziami
Riadok 275: | Riadok 275: | ||
Na načítavanie a vypisovanie dát sme doposiaľ používali výhradne konzolu. | Na načítavanie a vypisovanie dát sme doposiaľ používali výhradne konzolu. | ||
− | V praxi však často | + | 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. | |
+ | * V C++ existujú ekvivalenty <tt>cin >></tt> a <tt>cout << </tt> aj pre súbory, nájdete ich v knižnici [http://www.cplusplus.com/reference/fstream/fstream/ fstream]. | ||
===Základy: typ <tt>FILE *</tt> a funkcie <tt>fopen</tt>, <tt>fclose</tt>, <tt>fprintf</tt>, <tt>fscanf</tt> === | ===Základy: typ <tt>FILE *</tt> a funkcie <tt>fopen</tt>, <tt>fclose</tt>, <tt>fprintf</tt>, <tt>fscanf</tt> === | ||
− | So súbormi sa pri použití knižnice <tt>cstdio</tt> pracuje pomocou typu <tt>FILE *</tt>. | + | So súbormi sa pri použití knižnice <tt>cstdio</tt> pracuje pomocou typu <tt>FILE *</tt> (veľkými písmenami). |
+ | * Je to smerník na štruktúru typu <tt>FILE</tt>, ktorá obsahuje nejaké 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: | ||
<syntaxhighlight lang="C++"> | <syntaxhighlight lang="C++"> | ||
− | |||
FILE *fr, *fw; | FILE *fr, *fw; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | |||
'''Otvorenie súboru pre čítanie''' | '''Otvorenie súboru pre čítanie''' | ||
Riadok 292: | Riadok 292: | ||
* Otvorí súbor s názvom <tt>vstup.txt</tt> (prípadne možno zadať kompletnú cestu k súboru). | * Otvorí súbor s názvom <tt>vstup.txt</tt> (prípadne možno zadať kompletnú cestu k súboru). | ||
* Ak taký súbor neexistuje alebo sa nedá otvoriť, do <tt>fr</tt> priradí <tt>NULL</tt>. | * Ak taký súbor neexistuje alebo sa nedá otvoriť, do <tt>fr</tt> priradí <tt>NULL</tt>. | ||
− | * Z | + | * Z otvoreného súboru môžeme čítať napríklad pomocou <tt>fscanf</tt>, ktorá je analógiou k <tt>scanf</tt>. |
* Napríklad <tt>fscanf(fr, "%d", &x);</tt> | * Napríklad <tt>fscanf(fr, "%d", &x);</tt> | ||
Riadok 299: | Riadok 299: | ||
* Vytvorí súbor s názvom <tt>vystup.txt</tt>. Ak už existoval, zmaže jeho obsah (keby sme vo volaní <tt>fopen</tt> namiesto <tt>"w"</tt> použili <tt>"a"</tt>, pridávalo by sa na koniec existujúceho súboru). | * Vytvorí súbor s názvom <tt>vystup.txt</tt>. Ak už existoval, zmaže jeho obsah (keby sme vo volaní <tt>fopen</tt> namiesto <tt>"w"</tt> použili <tt>"a"</tt>, pridávalo by sa na koniec existujúceho súboru). | ||
* Ak sa nepodarí súbor otvoriť, do <tt>fw</tt> priradí <tt>NULL</tt>. | * Ak sa nepodarí súbor otvoriť, do <tt>fw</tt> priradí <tt>NULL</tt>. | ||
− | * Do | + | * Do otvoreného súboru môžeme zapisovať napr. pomocou funkcie <tt>fprintf</tt>, ktorá je analógiou k <tt>printf</tt>. |
* Napr. <tt>fprintf(fw, "%d", x);</tt> | * Napr. <tt>fprintf(fw, "%d", x);</tt> | ||
Riadok 314: | Riadok 314: | ||
using namespace std; | using namespace std; | ||
− | int main( | + | int main() { |
− | FILE *fr = fopen("vstup.txt", "r"); | + | // otvorime subory a skontrolujeme, ze nie su NULL |
+ | FILE *fr = fopen("vstup.txt", "r"); | ||
FILE *fw = fopen("vystup.txt", "w"); | FILE *fw = fopen("vystup.txt", "w"); | ||
assert(fr != NULL && fw != NULL); | assert(fr != NULL && fw != NULL); | ||
− | int n,r; | + | // nacitame pocet cisel |
+ | int n, r; | ||
r = fscanf(fr, "%d", &n); | r = fscanf(fr, "%d", &n); | ||
assert(r == 1 && n >= 0); | assert(r == 1 && n >= 0); | ||
+ | |||
+ | // alokujeme pole a nacitame cisla | ||
int *a = new int[n]; | int *a = new int[n]; | ||
− | + | for (int i = 0; i < n; i++) { | |
− | for (int i = 0; i < | ||
r = fscanf(fr, "%d", &a[i]); | r = fscanf(fr, "%d", &a[i]); | ||
assert(r == 1); | assert(r == 1); | ||
} | } | ||
fclose(fr); | fclose(fr); | ||
+ | |||
+ | // vypiseme cisla odzadu | ||
for (int i = n-1; i >= 0; i--) { | for (int i = n-1; i >= 0; i--) { | ||
fprintf(fw, "%d ", a[i]); | fprintf(fw, "%d ", a[i]); | ||
Riadok 334: | Riadok 339: | ||
fclose(fw); | fclose(fw); | ||
delete[] a; | delete[] a; | ||
− | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Riadok 344: | Riadok 348: | ||
FILE *stdin, *stdout; | FILE *stdin, *stdout; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | pre štandardný vstupný a výstupný prúd. Tie tak môžu byť použité v ľubovoľnom kontexte, v ktorom sa očakáva súbor. Napríklad volanie <tt>fscanf(stdin,"%d",&x)</tt> je ekvivalentné volaniu <tt>scanf("%d",&x)</tt>. | + | pre štandardný vstupný a výstupný prúd. Tie tak môžu byť použité v ľubovoľnom kontexte, v ktorom sa očakáva súbor. Napríklad volanie <tt>fscanf(stdin, "%d", &x)</tt> je ekvivalentné volaniu <tt>scanf("%d", &x)</tt>. |
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 <tt>FILE *</tt>. Typické použitie je napríklad nasledovné: | 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 <tt>FILE *</tt>. Typické použitie je napríklad nasledovné: | ||
Riadok 351: | Riadok 355: | ||
... | ... | ||
fscanf(fr, "%s", str); | fscanf(fr, "%s", str); | ||
− | if (strcmp(str,"-") == 0) { | + | if (strcmp(str, "-") == 0) { |
fw = stdout; | fw = stdout; | ||
} else { | } else { |
Verzia zo dňa a času 20:22, 13. november 2021
Obsah
Oznamy
- Na piatkových doplnkových cvičeniach bude bonusová rozcvička (za 1 bonusový bod). Zvyšné úlohy z tohto týždňa treba odovzdať do stredy 18. novembra, 22:00.
- Druhú domácu úlohu treba odovzdať do piatku 13. novembra, 22:00.
- Budúci týždeň budú kvôli štátnemu sviatku iba piatkové cvičenia. V pondelok 16. novembra bude zverejnených niekoľko úloh na cvičenia č. 9 (menej, než obvykle).
- V piatok 20. novembra bude na začiatku doplnkových cvičení krátky test, body za ktorý budú riadnou súčasťou hodnotenia z cvičení č. 9. Pokyny ohľadom technickej realizácie testu budú upresnené neskôr.
Smerníková aritmetika
Na smerníkoch možno vykonávať určité operácie, ktoré sa zvyknú nazývať smerníková aritmetika. Uvažujme číslo n typu int a smerníky p a q na nejaký typ T
int n;
T *p, *q;
- p + n je smerník na n-té políčko za adresou p, pričom veľkosť políčka je daná typom T
- p + n je teda to isté ako &(p[n]) a *(p+n) je to isté ako p[n].
- p++ je skratkou pre p = p + 1, posunie nám teda smerník p o políčko doprava.
- Výraz p[n] je len skratkou pre *(p+n).
- p[n] aj p + n teda chceme používať iba ak p ukazuje na prvok poľa, za ktorým v poli ide ešte aspoň n ďalších políčok
- Podobne p - n je smerník na n-té políčko pred adresou p.
- p - n teda chceme používať iba ak p ukazuje na prvok poľa, pred ktorým v poli ide ešte aspoň n ďalších políčok
- Ak p a q sú adresami prvkov v tom istom poli, p - q je celé číslo k také, že p == q + k, t.j. o koľko políčok je p ďalej vpravo od q.
- Ak p a q sú adresami prvkov v tom istom poli, môžeme ich tiež porovnávať pomocou <, >, <=, >=
- Ľubovoľné dva smerníky toho istého typu vieme porovnávať pomocou ==, !=.
Tu je napr. zvláštny spôsob ako vypísať pole a:
int a[4] = {4, 3, 2, 1};
for (int *smernik = a; smernik < a + 4; smernik++) {
cout << "Prvok " << smernik - a << " je " << *smernik << endl;
}
Podobný kód sa ale občas používa na prechádzanie reťazcov. Napríklad nasledujúca funkcia spočíta počet medzier v reťazci:
int zratajMedzery(char str[]) { // mohli by sme dat aj char *str
int pocet = 0;
while(*str != 0) { // kym nenajdeme ukoncovaciu nulu v retazci
if(*str == ' ') { // skontroluj znak, na ktory ukazuje smernik
pocet++;
}
str++; // posun smernik na dalsi znak
}
return pocet;
}
Funkcie z knižnice cstring so smerníkovou aritmetikou
- strstr(text, vzorka) vracia smerník na char
- NULL ak sa vzorka nenachádza v texte, smerník na začiatok prvého výskytu inak
- pozíciu výskytu zistíme smerníkovou aritmetikou:
char *text = "Hello world!"; char *vzorka = "or"; char *where = strstr(text, vzorka); if(where != NULL) { int position = where - text; }
- Ako by ste spočítali počet výskytov vzorky v texte?
- Podobne strchr hľadá prvý výskyt znaku v texte
Práca s konzolou na spôsob jazyka C: printf a scanf
- Doposiaľ sme s konzolou pracovali prostredníctvom knižnice iostream, ktorá patrí medzi štandardné knižnice jazyka C++ a v ktorej sú definované prúdy cin a cout.
- Dnes si ukážeme alternatívny prístup k práci s konzolou založený na knižnici cstdio, ktorá je štandardnou knižnicou jazyka C.
Výpis formátovaných dát na konzolu: printf
- S použitím knižnice cstdio možno na konzolu písať pomocou funkcie printf.
- Tu sú príklady jej použitia:
#include <cstdio>
#include <cmath>
using namespace std;
int main() {
int x = 10;
double y = sqrt(x);
printf("Ahoj svet, este raz!\n");
printf("Odmocnina cisla %d je %f.\n", x, y);
}
Tento program vypíše:
Ahoj svet, este raz!
Odmocnina cisla 10 je 3.162278.
Vo všeobecnosti vyzerá volanie printf nasledovne:
printf(format, hodnota1, hodnota2, ...)
- Prvý argument je formátovací reťazec, za ním môže nasledovať niekoľko ďalších argumentov.
- Bežné znaky z formátovacieho reťazca sa priamo vypíšu na výstup.
- Koniec riadku sa píše pomocou "\n".
- 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 (typ int).
- %ld: dlhé celé číslo (typ long int).
- %f: reálne číslo (typ double).
- %e: reálne číslo vo vedeckej notácii, napr. 5.4e7 (typ double).
- %c: znak (typ char).
- %s: reťazec (typ char *).
- %%: 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 vo vhodnom formáte vypíše hodnoty faktoriálu prirodzených čísel od 1 po 20 použitím typu long long int, ktorý garantuje aspoň 64 bitové číslo:
#include <cstdio>
using namespace std;
long long int factorial(int n) {
if (n == 0) {
return 1;
} else {
return n * factorial(n-1);
}
}
int main() {
for (int i = 1; i <= 20; i++) {
printf("%2d! = %22lld\n", i, factorial(i));
}
}
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 1307674368000
16! = 20922789888000
17! = 355687428096000
18! = 6402373705728000
19! = 121645100408832000
20! = 2432902008176640000
Nasledujúci program vypíše zadaný dátum vo formáte typu 02.01.2019:
#include <cstdio>
using namespace std;
void vypisDatum(int d, int m, int r) {
printf("%02d.%02d.%04d\n", d, m, r);
}
int main() {
vypisDatum(2, 1, 2019);
}
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íklad 0 (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, alebo h pre long, long long, resp. short.
- Z povinného symbolu konverzie (napr. d, f, s, ...).
Načítanie formátovaných dát z konzoly: scanf
Vstup z konzoly sa dá načítať funkciou scanf s typickým volaním
scanf(format, adresa1, adresa2, ...)
- Napríklad scanf("%d", &x) načíta celočíselnú hodnotu do premennej x.
- Zatiaľ čo argumentmi printf sú priamo hodnoty, scanf potrebuje adresy premenných, pretože ich modifikuje.
Pomocou scanf možno načítať aj viacero premenných naraz:
#include <cstdio>
using namespace std;
void vypisDatum(int d, int m, int r) {
printf("%02d.%02d.%04d\n", d, m, r);
}
int main() {
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.
- %d načíta int
- %lf načíta double (pozor, tu je rozdiel od printf, kde sa double vypisuje pomocou %f)
- %s načíta reťazec, konkrétne jedno slovo, t.j. postupnosť nebielych znakov. Ako argument sa zadá hodnota typu char*, ktorá má ukazovať na dostatočne veľké pole.
- %100s načíta slovo, ale najviac 100 znakov. Pole má mať veľkosť aspoň 101.
- %c načí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);
Kontrola správnosti vstupu
Funkcia scanf vracia 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 (typicky -1).
- Vstup z konzoly sa dá ukončiť pod Linuxom ako Ctrl+D resp. pod Windowsom ako Ctrl+Z a Enter
Príklad: zadávanie dátumu vo formáte deň.mesiac.rok s kontrolou vstupu:
#include <cstdio>
using namespace std;
void vypisDatum(int d, int m, int r) {
printf("%02d.%02d.%04d\n", d, m, r);
}
int main() {
int d, m, r;
printf("Zadaj datum: ");
if (scanf("%d.%d.%d", &d, &m, &r) == 3) {
printf("Datum je ");
vypisDatum(d,m,r);
} else {
printf("Nebol zadany korektny datum.\n");
}
}
Ďalší program počíta súčet postupne zadávaných čísel, až kým je zadané nekorektné číslo alebo koniec súboru:
#include <cstdio>
using namespace std;
int main() {
double sum = 0;
double x;
while (scanf("%lf", &x) == 1) {
sum += x;
}
printf("Sucet je %.2f\n", sum);
}
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.
- V C++ existujú ekvivalenty cin >> a cout << aj pre súbory, nájdete ich v knižnici fstream.
Základy: typ FILE * a funkcie fopen, fclose, fprintf, fscanf
So súbormi sa pri použití knižnice cstdio pracuje pomocou typu FILE * (veľkými písmenami).
- Je to smerník na štruktúru typu FILE, ktorá obsahuje nejaké 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 *fr, *fw;
Otvorenie súboru pre čítanie
- fr = 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 fr priradí NULL.
- Z otvoreného súboru môžeme čítať napríklad pomocou fscanf, ktorá je analógiou k scanf.
- Napríklad fscanf(fr, "%d", &x);
Otvorenie súboru pre zápis
- fw = fopen("vystup.txt", "w");
- Vytvorí súbor s názvom vystup.txt. Ak už existoval, zmaže jeho obsah (keby sme vo volaní fopen namiesto "w" použili "a", pridávalo by sa na koniec existujúceho súboru).
- Ak sa nepodarí súbor otvoriť, do fw priradí NULL.
- Do otvoreného súboru môžeme zapisovať napr. pomocou funkcie fprintf, ktorá je analógiou k printf.
- Napr. fprintf(fw, "%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 <cstdio>
#include <cassert>
using namespace std;
int main() {
// otvorime subory a skontrolujeme, ze nie su NULL
FILE *fr = fopen("vstup.txt", "r");
FILE *fw = fopen("vystup.txt", "w");
assert(fr != NULL && fw != NULL);
// nacitame pocet cisel
int n, r;
r = fscanf(fr, "%d", &n);
assert(r == 1 && n >= 0);
// alokujeme pole a nacitame cisla
int *a = new int[n];
for (int i = 0; i < n; i++) {
r = fscanf(fr, "%d", &a[i]);
assert(r == 1);
}
fclose(fr);
// vypiseme cisla odzadu
for (int i = n-1; i >= 0; i--) {
fprintf(fw, "%d ", a[i]);
}
fclose(fw);
delete[] a;
}
Štandardný vstup a výstup ako súbor
So štandardným vstupom a výstupom sa pracuje rovnako ako so súborom. V cstdio sú definované dva konštantné smerníky
FILE *stdin, *stdout;
pre štandardný vstupný a výstupný prúd. Tie tak môžu byť použité v ľubovoľnom kontexte, v ktorom 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é:
FILE *fr, *fw;
...
fscanf(fr, "%s", str);
if (strcmp(str, "-") == 0) {
fw = stdout;
} else {
fw = fopen(str, "w");
}
fprintf(fw, "Hello world!\n");
...
Testovanie konca súboru
Existujú dve možnosti testovania „nárazu” na koniec súboru:
- V knižnici cstdio je definovaná symbolická konštanta EOF, ktorá má väčšinou hodnotu -1. Ak sa funkcii fscanf nepodarí načítať žiadnu hodnotu, pretože načítavanie dospelo ku koncu súboru, vráti konštantu EOF ako svoj výstup.
- Funkcia feof(subor) vráti true práve vtedy, keď sa funkcia fscanf (alebo nejaká iná funkcia) už niekedy pokúšala čítať za koncom súboru subor.
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.
#include <cstdio>
#include <cassert>
using namespace std;
int main(void) {
FILE *f;
const int MAXN = 100;
int a[MAXN], N, kod;
f = fopen("vstup.txt", "r");
assert(f != NULL);
kod = fscanf(f, "%d ", &N);
assert(kod == 1 && N >= 0 && N < MAXN);
for (int i = 0; i < N; i++) {
kod = fscanf(f, "%d ", &a[i]);
assert(kod == 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;
kod = fscanf(f, "%d ", &x);
assert(kod == 1);
while (x != -1) {
assert(N < MAXN);
a[N] = x;
N++;
kod = fscanf(f, "%d ", &x);
assert(kod == 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 < MAXN);
kod = fscanf(f, "%d", &a[N]);
assert(kod == 1);
N++;
}
// zatvorime subor a spracujeme data
Po poslednom čísle v súbore často nasleduje ešte koniec riadku, v dôsledku čoho môže posledné volanie funkcie fscanf vyústiť v návratovú hodnotu -1 (predchádzajúce volanie fscanf totiž ešte „nenarazilo” na koniec súboru, v dôsledku čoho je pred čítaním posledného riadku hodnota feof(f) stále rovná false). Tým pádom program zlyhá na riadku assert(kod == 1). 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 <cstdio>
#include <cassert>
using namespace std;
int main(void) {
FILE *f;
const int MAXN = 100;
int a[MAXN], N, kod;
f = fopen("vstup.txt", "r");
assert(f != NULL);
N = 0;
while (!feof(f)) {
assert(N < MAXN);
kod = fscanf(f, "%d ", &a[N]);
assert(kod == 1);
N++;
}
fclose(f);
// tu pride spracovanie dat v poli a
}