Czym są strumienie?

edytuj

Najproś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"

edytuj

Zapewne 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"

edytuj

Funkcja 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

edytuj
 
Program w C++ zapisujący dane do pliku graficznego

Za 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 :

 
Jak utworzyć plik ppm za pomocą strumienia plikowego
/*
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

edytuj

Wyróż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;
}