Strona 1
[lekcja] Obsługa strumienia wejściowego | Kurs C++ » Poziom 1
2011-11-12 15:32:28
http://cpp0x.pl/kursy/Kurs-C++/Poziom-1/Obsluga-strumienia-wejsciowego/12
Darmowe kursy Online
»
Kurs C++
»
Poziom 1
Autor: Piotr Szawdyński
Obsługa strumienia wejściowego
[lekcja]
Rozdział 9. Omówienie obsługi standardowegowejścia za pomocą strumienia std::cin oraz przedstawieniesposobu walidacji wczytywanychdanych.
Idąc małymi krokami do przodu nadszedł czas na obsługę strumienia wejściowego, czyli mówiąc prościej na wczytywanie danych.
Zagadnienie to o ile jest stosunkowo proste do zrozumienia to wymaga ono sporej otoczki związanej z kontrolowaniem błędnie
wprowadzonych danych. Zanim jednak dojdziemy do tych zagadnień zajmijmy się najpierw podstawami.
Wczytywaniedanych
Wczytywanie danych do zmiennych odbywa się za pomocą strumienia std::cin. Aby móc wczytać dane znajdujące się na standardowym
wejściu, należy wykorzystać do tego celu operator >>. Wczytanie danych wygląda więc następująco:
C/C++
std
::
cin
>>
nazwa_zmiennej
;
W powyższy sposób możemy wczytywać wszystkie podstawowe typy danych - poczynając od liczb całkowitych, przechodząc przez liczby
rzeczywiste oraz znaki, a kończąc na tekście . W niniejszym rozdziale skupimy się tylko i wyłącznie na wczytywaniu liczb całkowitych i
rzeczywistych . Tekstem zajmiemy się w dalszej części kursu.
Rozpatrzmy teraz następujący przykład:
C/C++
#include <iostream>
int
main
()
{
int
a
;
float
b
;
std
::
cout
<<
"Podaj liczbe calkowita: "
;
std
::
cin
>>
a
;
std
::
cout
<<
"Podaj liczbe rzeczywista: "
;
std
::
cin
>>
b
;
std
::
cout
<<
"Liczba a = "
<<
a
<<
std
::
endl
;
std
::
cout
<<
"Liczba b = "
<<
b
<<
std
::
endl
;
return
0
;
}
Powyższy program zapyta się użytkownika o podanie dwóch liczb. Pierwszą z nich ma być liczba całkowita, a drugą liczba rzeczywista .
Jeśli użytkownik będzie posłuszny, program wyświetli przykładowo następującą treść:
Podaj liczbe calkowita: 13
Podaj liczbe rzeczywista: 16.6
Liczba a = 13
Liczba b = 16.6
Problem zaczyna się pojawiać jednak wtedy, gdy mamy do czynienia z Yeti, czyli kimś kto niekoniecznie chce używać programu zgodnie z
wolą programisty.
Sprawdzaniepoprawnościwprowadzonychdanych
Wczujmy się przez chwilę w złośliwego użytkownika i zacznijmy działać tak jak on - wprowadźmy więc zamiast liczby literę. Ja
wprowadziłem literę a i otrzymałem następujący wynik na ekranie:
Podaj liczbe calkowita: a
Podaj liczbe rzeczywista: Liczba a = 4273126
Liczba b = 3.21401e-039
Jak widać już na pierwszy rzut oka program zrobił dwie złe rzeczy. Pierwsza - nie wczytał liczby całkowitej; druga - pominął wczytywanie
liczby rzeczywistej. Jako programiści powinniśmy zadać jednak pytanie: czy udało się wczytać dane?. Dopiszmy więc dwa nowe
wiersze do programu po każdym wczytywaniu danych:
C/C++
std
::
cout
<<
"Czy udalo sie wczytac? "
<<
std
::
cin
.
good
() <<
std
::
endl
;
std
::
cout
<<
"Czy cos nawalilo? "
<<
std
::
cin
.
fail
() <<
std
::
endl
;
Po dokonaniu powyższej zmiany program będzie wyglądał tak:
C/C++
Strona 2
[lekcja] Obsługa strumienia wejściowego | Kurs C++ » Poziom 1
2011-11-12 15:32:28
http://cpp0x.pl/kursy/Kurs-C++/Poziom-1/Obsluga-strumienia-wejsciowego/12
#include <iostream>
int
main
()
{
int
a
;
float
b
;
std
::
cout
<<
"Podaj liczbe calkowita: "
;
std
::
cin
>>
a
;
std
::
cout
<<
"Czy udalo sie wczytac? "
<<
std
::
cin
.
good
() <<
std
::
endl
;
std
::
cout
<<
"Czy cos nawalilo? "
<<
std
::
cin
.
fail
() <<
std
::
endl
;
std
::
cout
<<
"Podaj liczbe rzeczywista: "
;
std
::
cin
>>
b
;
std
::
cout
<<
"Czy udalo sie wczytac? "
<<
std
::
cin
.
good
() <<
std
::
endl
;
std
::
cout
<<
"Czy cos nawalilo? "
<<
std
::
cin
.
fail
() <<
std
::
endl
;
std
::
cout
<<
"Liczba a = "
<<
a
<<
std
::
endl
;
std
::
cout
<<
"Liczba b = "
<<
b
<<
std
::
endl
;
return
0
;
}
W programie pojawiły się dwa nowe zapisy: std::cin.good() oraz std::cin.fail(). good() i fail() są metodami należącymi do
strumienia std::cin. Za pomocą nich możemy odczytać stany strumienia. Są nimi odpowiednio: czy wczytywanie się powiodło (dla
metody good()), oraz czy wystąpiły jakieś błędy (dla metody fail()).
Posiadając wiedzę na temat tego czy udało się wczytać poprawnie dane czy też nie - będziemy mogli podjąć w przyszłości odpowiednie
działania. Na chwilę obecną nie potrafisz jednak sterować przebiegiem programu dlatego też powrócimy do tego zagadnienia ponownie,
gdy będziesz miał odpowiednią wiedzę .
Opis działaniastrumieniawejściowego
Skoro nauczyliśmy się już korzystać ze strumienia wejściowego w podstawowym wymiarze - przyjrzyjmy się teraz jego działaniu.
Wyobraźmy więc sobie, że początkowo strumień jest pusty. Wysyłamy następnie żądanie:
"daj mi liczbę całkowitą"
(czyli:
std::cin>>liczba). Strumień jest pusty, więc nie można z niego pobrać danych, a więc użytkownik musi wprowadzić nowe dane do
strumienia. Wprowadźmy teraz do strumienia następujące dane:
12345, 321. Czy 2+2 wynosi 4?
Po wciśnięciu klawisza ENTER dane te trafiają do bufora strumienia wejściowego, z którego następnie odczytywane są dane. Po
wczytaniu liczby w buforze strumienia wejściowego zostaną następujące dane:
, 321. Czy 2+2 wynosi 4?
Co się teraz stanie, gdy zechcemy wczytać kolejną liczbę? Strumień stwierdzi, że pierwszym znakiem w strumieniu jest znak
,
, który
nie jest liczbą, a więc nie zostanie wczytana liczba. Operacja wczytywania się nie powiedzie , a flaga błędu zostanie ustawiona.
Białe znaki
Białymi znakami nazywamy te, które nie mają swojej reprezentacji wizualnej, a istnieją w tekście . Białe znaki to: spacja, enter i
tabulacja.
Strumień, a białe znaki
Gdy używamy strumienia std::cin>> białe znaki są pomijane. Tak więc gdyby w buforze strumienia nie znajdował się przecinek, tylko
biały znak - strumień by go po prostu pominął i przeszedł do kolejnego znaku. W konsekwencji druga operacja wczytywania liczby
powiodłaby się, a w strumieniu pozostałyby dane:
. Czy 2+2 wynosi 4?
Czyszczeniezawartości strumieniawejściowego
Jeśli chcemy mieć większą kontrolę nad strumieniem wejściowym to powinniśmy czyścić jego zawartość przed każdym wczytaniem
danych. Aby to zrobić musimy wywołać dwie metody strumienia std::cin. Pierwszą z nich jest std::cin.clear(), która czyści flagi błędu.
Drugą metodą jest std::cin.sync(), która czyści bufor strumienia. Kod, który wyczyści zawartość bufora będzie wyglądał następująco:
C/C++
std
::
cin
.
clear
()
;
std
::
cin
.
sync
()
;
Problemy z czyszczeniemstrumieniapod Linuksem
Jeżeli jesteś szczęśliwym posiadaczem Linuksa i uczysz się programowania właśnie pod nim, niestety możesz doświadczyć nieprzyjemnej
sytuacji, w której wiersz
std
::
cin
.
sync
()
;
po prostu nie zadziała. Rozwiązaniem powyższego problemu może być użycie metody
ignore
, dostępnej w strumieniu std::cin. Wspomnianą metodę należy użyć w następujący sposób:
C/C++
//tu wczytanie danych ze strumienia za pomoc
ą
std::cin>>
std
::
cin
.
clear
()
;
std
::
cin
.
ignore
(
1000
,
'\n'
)
;
Strona 3
[lekcja] Obsługa strumienia wejściowego | Kurs C++ » Poziom 1
2011-11-12 15:32:28
http://cpp0x.pl/kursy/Kurs-C++/Poziom-1/Obsluga-strumienia-wejsciowego/12
Architektserwisu: Piotr Szawdyński
© Wszelkieprawa zastrzeżone2005-2011
Powyższy zapis ignoruje do 1000 znaków znajdujących się w strumieniu wejściowym. Jeżeli w strumieniu wejściowym zostanie napotkany
znak nowego wiersza '\n' proces ignorowania znaków zostanie zakończony. Proces ignorowania znaków nie zostanie jednak zakończony
gdy w buforze nie będzie więcej znaków do odczytania. Jeżeli chcesz używać tej techniki to należy czyścić strumień wejściowy po
każdym wczytaniu danych by uniknąć ewentualnych problemów technicznych związanych ze sposobem działania tej metody.
Jeżeli nie chcesz uzależniać się od konkretnej stałej liczbowej , która mówi ile maksymalnie znaków może zostać pominiętych, możesz
użyć poniższego zapisu:
C/C++
#include <limits>
int
liczba
;
std
::
cin
>>
liczba
;
bool
bCzySukces
=
std
::
cin
.
good
()
;
std
::
cin
.
clear
()
;
std
::
cin
.
ignore
(
std
::
numeric_limits
<
std
::
streamsize
>::
max
()
,
'\n'
)
;
//(...) tu dalsza cz
ęść
programu
//INFO: zmienna bCzySukces zawiera informacj
ę
czy udało si
ę
wczyta
ć
dane
Powyższy kod spowoduje wczytanie liczby, a następnie pominie wszystkie znaki znajdujące się w buforze aż do napotkania znaku
przejścia do nowej linii '\n'.
Pamiętaj, że użycie
std::numeric_limits
wymaga dodatkowo dołączenia biblioteki
limits
na początku programu.
Pamiętaj, że:
Efekt działania zapisów
cin
.
clear
()
;
oraz
cin
.
sync
()
;
będzie możliwy do zaobserwowania tylko i wyłącznie po wprowadzeniu
błędnych danych do programu. Przykładem błędnych danych jest wprowadzanie tekstu zamiast liczb.
Zadanie domowe
Napisz program, który wczyta trzy liczby rzeczywiste , a na końcu programu je wszystkie wypisze . Zadbaj o to, by bufor strumienia
wejściowego był za każdym razem czyszczony. Wynik końcowy powinien również zawierać informacje czy wczytanie danej liczby się
powiodło.
Przykładowedane wejściowe
13.3
tak 123
33.22nie
Przykładowedane wyjściowe
Liczba pierwsza to: 13.3. Wczytano? 1.
Liczba druga liczba to: 0. Wczytano? 0.
Liczba trzecie liczba to: 33.22. Wczytano? 1.
Wskazówka
Informacje o poprawnym (albo niepoprawnym) wczytaniu danych należy przechować w dodatkowych zmiennych typu bool.
Wszystkieteksty są chronioneprawami autorskimi. Kopiowanielub rozpowszechnianietreści poza niniejszymserwisem jest
zabronione.
Powyższe ograniczenie nie dotyczy autora opracowania, któremu przysługuje prawo do rozpowszechnianiawłasnego tekstu wedle własnego uznania.