Menu

Wejście i wyjście w C++: cin, cout, getline i strumienie

Jak działa wejście i wyjście konsoli w C++: wypisywanie przez cout, wczytywanie przez cin, klasyczny błąd ze znakiem nowej linii przy getline po cin i odzyskiwanie po błędnych danych.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Jak C++ rozmawia z konsolą

C++ obsługuje wejście i wyjście konsoli przez strumienie z nagłówka <iostream>. Piszesz do świata przez cout (wyjście znakowe), a czytasz od użytkownika przez cin (wejście znakowe). Używają dwóch operatorów, które znasz już jako przesunięcia bitowe, tutaj użytych w nowej roli:

  • << to operator wstawiania: wysyła dane do cout.
  • >> to operator wyodrębniania: wyciąga dane z cin do zmiennej.

Łatwo zapamiętać kierunek: strzałki wskazują, w którą stronę płyną dane. Skoro umiesz już trzymać tekst w napisach, pora przenosić go do programu i z powrotem.

Wypisywanie przez cout

cout wysyła na standardowe wyjście wszystko, co do niego wstawisz. W jednej instrukcji możesz połączyć wiele <<, swobodnie mieszając tekst, liczby i zmienne.

Każde << dopisuje do tej samej linii, dopóki nie wstawisz znaku nowej linii. Masz na to dwa sposoby: '\n' wstawia znak nowej linii, a endl wstawia znak nowej linii i opróżnia bufor na ekran. Opróżnianie ma realny koszt, więc w pętli, która wypisuje tysiące linii, wybieraj '\n': strumień opróżnia się sam, gdy trzeba (i zawsze przy zakończeniu programu).

// Wystarczy przy jednorazowym komunikacie:
cout << "Done" << endl;

// W intensywnej pętli lepiej tak, bez wymuszonego opróżniania w każdej iteracji:
for (int i = 0; i < 1000000; ++i)
    cout << i << '\n';

Wczytywanie przez cin i >>

cin >> variable wczytuje jeden token oddzielony białymi znakami i konwertuje go na typ zmiennej. Pomija początkowe spacje i znaki nowej linii, a zatrzymuje się na następnym białym znaku.

Ponieważ >> zatrzymuje się na białym znaku, świetnie nadaje się do pojedynczych liczb i słów, ale do całego zdania jest bezużyteczny: cin >> word dla danych hello world wczytuje tylko hello, a world zostawia w buforze na następny odczyt.

Wczytywanie całej linii przez getline

Aby złapać całą linię, razem ze spacjami, użyj getline(cin, line), które wczytuje wszystko aż do klawisza Enter do std::string.

To właściwe narzędzie zawsze, gdy dane mogą zawierać spacje: imiona i nazwiska, adresy, zdania. Czeka tu tylko jedna pułapka, która pojawia się w chwili, gdy połączysz getline z >>.

Strona o getline omawia tę funkcję osobno: własne separatory, wczytywanie do końca danych i czytanie pliku linia po linii przez std::ifstream.

Pułapka ze znakiem nowej linii przy cin + getline

To najczęstszy błąd wejścia i wyjścia w C++. Gdy wykonujesz cin >> n, wyodrębnianie wczytuje liczbę, ale zostawia znak nowej linii (wciśnięty Enter) w buforze wejścia. Następne getline od razu widzi ten pozostały znak nowej linii, uznaje linię za zakończoną i oddaje ci pusty napis, w ogóle nie czekając na dane.

int age;
string city;

cin >> age;            // wpisujesz 30 i wciskasz Enter; '\n' zostaje w buforze
getline(cin, city);    // czyta pozostałe '\n' -> city to "" (pusty!)

Rozwiązaniem jest odrzucenie tego pozostałego znaku nowej linii przez cin.ignore po >>, a przed getline:

cin.ignore(numeric_limits<streamsize>::max(), '\n') pomija znaki, dopóki nie zje znaku nowej linii (albo nie dojdzie do końca danych). To solidna wersja: krótsze cin.ignore() odrzuca tylko jeden znak, co się psuje, gdy użytkownik wpisze po liczbie dodatkowe spacje. Z przyzwyczajenia sięgaj po pełną formę.

Gdy wczytywanie się nie uda

Jeśli użytkownik wpisze litery tam, gdzie oczekujesz liczby, wyodrębnianie się nie udaje: cin przechodzi w stan błędu, a zmienna docelowa nie dostaje wczytanej wartości (od C++11 jest ustawiana na 0). Co gorsza, gdy cin jest w stanie błędu, każdy kolejny odczyt też jest po cichu pomijany, więc łatwo o nieskończoną pętlę.

Odzyskiwanie zawsze ma dwa kroki: cin.clear() czyści flagi błędu, żeby strumień znów nadawał się do użytku, a cin.ignore(...) wyrzuca problematyczne znaki, które nadal tkwią w buforze. Pomiń ignore, a błędne dane zostaną na miejscu, więc kolejne >> znowu się nie powiedzie: to klasyczna nieskończona pętla. Sprawdzanie cin >> n bezpośrednio w warunku działa, bo strumień w stanie błędu konwertuje się na false.

Częste błędy, których warto unikać

  • Używanie cin >> s do zdania. Zatrzymuje się na pierwszej spacji. Do wszystkiego, co zawiera spacje, używaj getline.
  • Zapominanie o cin.ignore między >> a getline. Pozostały znak nowej linii daje ci pustą linię. Najpierw wyczyść bufor.
  • Używanie endl wszędzie. Każde wymusza opróżnienie bufora. Domyślnie pisz '\n', a endl zostaw na sytuacje, gdy wynik naprawdę musi pojawić się od razu.
  • Ignorowanie nieudanego cin. Litery przy wczytywaniu liczby psują cin; przed kolejnym odczytem zawsze wywołaj clear(), a potem ignore().

Najczęściej zadawane pytania

Dlaczego getline pomija dane zaraz po użyciu cin w C++?

cin >> x wczytuje liczbę, ale zostawia w buforze znak nowej linii z wciśniętego Entera. Następne getline czyta do tego pozostałego znaku nowej linii i od razu zwraca pusty napis. Najpierw go usuń: cin.ignore(numeric_limits<streamsize>::max(), '\n'); po >>, a przed getline.

Czym się różni endl od \n w C++?

Oba kończą linię, ale endl dodatkowo opróżnia bufor wyjścia na ekran, a '\n' wstawia tylko znak nowej linii. Opróżnianie kosztuje: w intensywnej pętli wybieraj '\n' i pozwól strumieniowi opróżniać się samodzielnie. Używaj endl tylko wtedy, gdy wynik naprawdę musi pojawić się natychmiast.

Jak wczytać całą linię tekstu ze spacjami w C++?

Użyj getline(cin, line), a nie cin >> line. Operator >> zatrzymuje się na pierwszym białym znaku, więc łapie tylko jedno słowo. getline wczytuje wszystko aż do klawisza Enter do std::string, razem ze spacjami.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ