C++/Strumienie
Czym są strumienie?
edytujNajprościej mówiąc jest to ciąg bajtów o nieokreślonej długości. Strumień danych jest to szereg danych przesyłanych po sobie, który może być skończony lub nieskończony.
Przykładowo: film to szereg zdjęć (kadrów, klatek) wyświetlanych po sobie z określoną częstotliwością (podstawa to 25 zdjęć wyświetlanych na sekundę), Jeżeli zdjęcia będą odpowiednio szybko wyświetlane z odpowiednią kolejnością, to mamy wrażenie, że wszystko działa płynnie, a sam film ogląda się przyjemnie, Dzięki temu mówimy, że zdjęcia z filmu są przesyłane strumieniowo.
Zarządzać strumieniami możemy tak samo, jak w języku C, za pomocą struktur typu FILE i poleceń fopen() i fclose(), lecz daje to małe możliwości, o czym się przekonamy podczas nauki programowania obiektowego. Dlatego w C++ utworzono dużo wygodniejszy mechanizm, z którego już skorzystaliśmy. Wyróżniamy trzy rodzaje strumieni:
Strumienie "konsoli"
edytujZapewne każdy uważny czytelnik wie już, jak pobierać oraz wyświetlać dane na ekranie konsoli. Dla przypomnienia napiszę. Do wczytywania danych ze strumienia wejścia służy operator >>, a wysyłania danych do strumienia wyjścia służy operator <<. Jednak metody, które do tej pory poznałeś nie zawsze spełnią twoje oczekiwania. Jak myślisz, co wyświetli poniższy program?
#include <iostream>
#include <string>
int main ()
{
std::string x;
std::cout << "Podaj swoje imie i nazwisko: ";
std::cin >> x;
std::cout << x << std::endl;
return 0;
}
Prawdopodobnie Cię rozczaruję - wyświetli tylko i wyłącznie imię! Operator >> "wyciąga" pojedyncze słowo oddzielone białymi znakami oraz zapisuje je do zmiennej x. Musimy stworzyć kolejną zmienną typu string i zapisać w niej nazwisko i użyć kaskadowej operacji wstawiania danych do strumienia. Wystarczy dokonać kilka modyfikacji tego programu:
- zmienić linijkę:
std::string x;
na:
std::string a, b;
- zmienić linijkę:
std::cin >> x;
na
std::cin >> a >> b;
- zmienić linijkę:
std::cout << x << std::endl;
na
std::cout << a << ' ' << b << std::endl;
Obiekty tego typu dziedziczą po klasie ostream dla strumieni wyjścia i istream dla wejścia. Plik nagłówkowy iostream sprawia, że mamy od początku otwarte 3 strumienie:
- std::cin - standardowe wejście
- std::cout - standardowe wyjście
- std::cerr - gdy coś złego się stanie (wyjście)
Funkcja "getline"
edytujFunkcja ta umożliwia pobranie z klawiatury tekstu zawierającego spacje (obiekt "cin" przestaje wczytywać tekst po napotkaniu pierwszej spacji, tabulatora lub znaku końca wiersza). Oto przykład użycia funkcji "getline":
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "Podaj tekst: ";
string tekst;
getline(cin, tekst);
cout << tekst << endl;
}
Jeśli przed użyciem funkcji "getline" użyjemy obiektu "cin", ten ostatni pozostawia zwykle znak końca wiersza '\n' w buforze klawiatury. Funkcja "getline" napotykając ten znak natychmiast kończy działanie, więc żeby uniknąć błędnego działania programu, należy wywołać funkcję cin.ignore(). Zostało pokazane to w poniższym przykładzie:
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "Podaj liczbę: ";
int liczba;
cin >> liczba;
cout << "Podaj tekst: ";
string tekst;
cin.ignore(); // to wywołanie usunie z bufora znak '\n' pozostawiony przez obiekt "cin"
getline(cin, tekst);
cout << liczba << ' ' << tekst << endl;
return 0;
}
Strumienie plikowe
edytujZa pomocą strumieni możemy czytać i zapisywać do plików:
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::string a;
std::cout << "Nacisnij Enter aby zakonczyc zapis.\n";
std::ofstream f ("log.txt");
std::cin >> a;
if (f.good())
{
f << a;
f.close();
}
return 0;
}
Program zapisuje łańcuch znaków do pliku. Pobiera go do momentu naciśnięcia Enter.
Inny przykład :
/*
https://commons.wikimedia.org/wiki/File:XOR_texture.png
g++ p.cpp -Wall
./a.out
*/
#include <fstream>
int main()
{
std::ofstream file;
file.open("xor.ppm");
file << "P2 256 256 255\n";
for (int i = 0; i < 256; i++)
for (int j = 0; j < 256; j++)
file << (i ^ j) << ' ';
file.close();
return 0;
}
Strumienie napisów
edytujWyróżniamy jeszcze jeden rodzaj strumieni - stringstream. Dzięki niemu jesteśmy w stanie operować na napisach tak, jak na zwykłym strumieniu. Wyobraźmy sobie sytuację, gdy musimy zamienić liczbę całkowitą na napis. Język C umożliwiał nam dokonywanie takich operacji za pomocą funkcji sprintf() bądź niestandardowej funkcji itoa(). Jednak zaprezentowane poniżej rozwiązanie jest o wiele czytelniejsze.
#include <iostream>
#include <sstream>
int main ()
{
long x; // Zmienna do przechowania liczby
std::string napis; // Zmienna do przechowania napisu
std::stringstream ss; // Strumień do napisów
std::cout << "Podaj dowolna liczbe calkowita: ";
std::cin >> x;
ss << x; // Do strumienia 'wysyłamy' podaną liczbę
napis = ss.str(); // Zamieniamy zawartość strumienia na napis
std::cout << "Dlugosc napisu wynosi " << napis.size() << " znakow." << std::endl;
return 0;
}