Skip to content

Commit e096ed5

Browse files
committed
Added number read
1 parent 8da336b commit e096ed5

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

files/read_numbers.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Čítanie čísel
2+
3+
Predtavme si, že máme súbor `numbers.txt` s obsahom:
4+
5+
```
6+
123 456
7+
789 4444
8+
9+
10 11
10+
```
11+
12+
A chceme tieto čísla prečítať a vypísať na konzolu oddelené novým riadkom. Najjednoduchší spôsob je použiť `operator>>`, ktorý sme si už ukázali. Môžeme s ním čítať jednotlivé čísla, pričom ignoruje whitespace.
13+
14+
```cpp
15+
#include <fstream>
16+
#include <iostream>
17+
18+
int main() {
19+
std::ifstream f("numbers.txt");
20+
21+
int n;
22+
while (f >> n){
23+
std::cout << n << '\n';
24+
}
25+
}
26+
```
27+
28+
## Kontrola stavu súboru
29+
30+
Program funguje, ale má jeden problém. Ak sa niečo nepodarí, tak sa `f` dostane do `fail` stavu a cyklus skončí. To znamená, že ak by sme v súbore mali nejaký nečíselný znak, tak program skončí skôr ako prečíta všetky čísla. Preto je dobré na konci skontrolovať či sme naozaj došli až na koniec súboru.
31+
32+
```cpp
33+
#include <fstream>
34+
#include <iostream>
35+
36+
int main() {
37+
std::ifstream f("numbers.txt");
38+
39+
int n;
40+
while (f >> n) {
41+
std::cout << n << '\n';
42+
}
43+
44+
if (!f.eof()) {
45+
std::cerr << "Cannot read whole input file.\n";
46+
return 1;
47+
}
48+
}
49+
```
50+
51+
Teraz ak by sme mali v súbore napríklad `123 456 abc`, tak program vypíše `123` a `456`, ale na konci vypíše chybu, že sa nepodarilo prečítať celý súbor. Čo je správne, keďže `abc` nie je číslo.
52+
53+
## `int` overflow
54+
55+
Ďalší problém môže nastať ak by sme mali v súbore číslo, ktoré je väčšie ako `INT_MAX` alebo menšie ako `INT_MIN`. Vtedy sa `f` dostane do `fail` stavu a program skončí. Je tu ale jeden problém ako je to číslo, ktore pretečie na konci súboru, tak to nevieme úple dobre detekovať.
56+
57+
Súbor `numbers.txt` (nakonci **je** nový riadok):
58+
59+
```
60+
123 456
61+
789 4444
62+
21474836479999
63+
64+
```
65+
66+
Na tomto súbore program skončí s chybou, teda správne. Ale ak nový riadok na konci súboru odstránime, program skončí bez chyby, čo je zlé a vypíše prvé 4 čísla. Dôvod je ten, že síce na konci bude mať nastevený `fail` stav, ale zároveň bude aj `eof` stav, takže kontrola na konci prejde. Mohlo by nás napadnúť, že by sme na konci skontrolovali či je `fail` (namiesto `eof`) stav nastavený a ak áno, tak by sme vrátili chybu.
67+
68+
```cpp
69+
#include <fstream>
70+
#include <iostream>
71+
72+
int main() {
73+
std::ifstream f("numbers.txt");
74+
75+
int n;
76+
while (f >> n) {
77+
std::cout << n << '\n';
78+
}
79+
80+
if (f.fail()) {
81+
std::cerr << "Cannot read whole input file.\n";
82+
return 1;
83+
}
84+
}
85+
```
86+
87+
Toto funguje celkom dobre, jediný problém sú whitespace na konci súboru. Ak by sme mali na konci súboru napríklad medzeru, tak program skončí s chybou, čo je zlé, keďže whitespace na konci súboru by nemal robiť problém. Riešenie je trochu komplikovanejšie ako len check stavu.
88+
89+
## Čítanie stringov
90+
91+
Môžeme si pomôcť tak, že budeme čítať stringy a tie potom skonvertujeme na čísla. Toto riešenie funguje, je ale o niečo komplikovanejšie.
92+
93+
```cpp
94+
#include <fstream>
95+
#include <iostream>
96+
#include <string>
97+
#include <climits> // for INT_MIN, INT_MAX
98+
#include <cstdlib> // for std::strtol
99+
100+
int main() {
101+
std::ifstream f("numbers.txt");
102+
103+
std::string s;
104+
while (f >> s) {
105+
char* end;
106+
errno = 0; // reset errno before call
107+
long val = std::strtol(s.c_str(), &end, 10);
108+
if (*end != '\0' // check if whole string was converted
109+
|| errno == ERANGE // check for long overflow
110+
|| val < INT_MIN || val > INT_MAX) { // clamp to int range
111+
std::cerr << "Invalid number: " << s << '\n';
112+
return 1;
113+
}
114+
std::cout << static_cast<int>(val) << '\n';
115+
}
116+
117+
if (!f.eof()) {
118+
std::cerr << "Cannot read whole input file.\n";
119+
return 1;
120+
}
121+
}
122+
```
123+
124+
## Ignorovanie whitespace
125+
126+
Ďalšie riešenie je trochu zmeniť vyčítanie, aby sme vždy najprv vyčítali všetky whitespace znaky. To zabezpečíme pomocou `std::ws`, ktorý je špeciálny manipulator, ktorý vyčítava všetky whitespace znaky.
127+
128+
```cpp
129+
#include <fstream>
130+
#include <iostream>
131+
132+
int main() {
133+
std::ifstream f("numbers.txt");
134+
135+
while (!(f >> std::ws).eof()) {
136+
int n;
137+
f >> n;
138+
if (f.fail()) {
139+
return EXIT_FAILURE;
140+
}
141+
std::cout << n << '\n';
142+
}
143+
144+
if (!f.eof()) {
145+
std::cerr << "Cannot read whole input file.\n";
146+
return 1;
147+
}
148+
}
149+
```
150+
151+
*C spôsob pomocou `fscanf` tu robiť nebudeme, tam je priveľa nedefinováneho a nešpecifikovaného správania, aby sme to mohli odporučiť.*

index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
## Práca so súbormi
1111

1212
* [Práca s textovými súbormi](/files/text_files.md)
13+
* [Čítanie čísel](/files/read_numbers.md)
1314
* [Vlastné stream operátory `<<` a `>>`](/files/stream_operators.md)
1415
* [Binárne súbory](/files/binary_files.md)
1516

0 commit comments

Comments
 (0)