====== Strumienie napisowe ====== ===== Wstęp ===== Strumienie napisowe ( stringstream ) są zestawem trzech klas, które umożliwiają wykonywanie operacji na napisach ( string ) tak, jakby były one strumieniami. Takie rozwiązanie bardzo ułatwia manipulację napisami, co w szczególności oznacza że można używać operatorów strumieniowych << i >> do konwertowania różnych typów obiektów na napisy i odwrotnie. Dane zapisane w strumieniu napisowym są przechowywane przez wewnętrzny obiekt klasy stringbuf, który jest w miarę potrzeb automatycznie powiększany. Główne zastosowania strumieni napisowych to: * Formatowanie napisów * Parsowanie napisów * Konwersja napisów na inne typy ===== Konstrukcja ===== Nagłówek sstream dostarcza deklaracje trzech klas strumieni napisowych: * **ostringstream** - strumień dziedziczący po ostream, umożliwiający jedynie zapis * **istringstream** - strumień dziedziczący po istream, umożliwiający jedynie odczyt * **stringstream** - strumień dziedziczący po iostream, umożliwiający zarówno zapis jak i odczyt Konstruktory tych klas są niemal identyczne i pobierają referencję na napis, którego zawartość zostanie skopiowana jako początkowa zawartość strumienia oraz tryb w jakim strumień będzie otwarty. Dzięki zastosowaniu parametrów domyślnych można tworzyć obiekty strumieni napisowych zmieniając tylko ważne dla programisty parametry lub nie podając ich wcale. //konstruktory klasy stringstream explicit stringstream ( openmode which = ios_base::out|ios_base::in ); explicit stringstream ( const string & str, openmode which = ios_base::out|ios_base::in ); //konstruktory klasy ostringstream explicit ostringstream ( openmode which = ios_base::out ); explicit ostringstream ( const string & str, openmode which = ios_base::out ); //konstruktory klasy istringstream explicit istringstream ( openmode which = ios_base::in ); explicit istringstream ( const string & str, openmode which = ios_base::in ); ==== Szerokie znaki ==== Biblioteka sstream dostarcza także definicji strumieni napisowych dla szerokich znaków, odpowiednio: * wostringstream * wistringstream * wstringstream ===== Metody publiczne ===== ==== rdbuf ==== stringbuf* rdbuf ( ) const; Funkcja zwraca wsaźnik na obiekt stringbuf w którym strumień przechowywuje dane. ==== str ==== string str ( ) const; void str ( const string & s ); Pierwsza wersja fukcji zwraca kopię napisu przechowywanego w strumieniu. Druga wersja pobiera referencję na obiekt napisu, który zostanie skopoiwany do bufora strumienia. Funkcja ta nie czyści flag ustawionych w strumieniu. ==== Metody odziedziczone ==== Strumienie napisowe dziedziczą po klasach strumieni biblioteki iostream i udostępniają wszystkie metody standardowych strumieni. Dokładny opis wszystkich funkcji można znaleźć w bibliografii poniżej. ===== Formatowanie napisów ===== Interfejs klasy string nie udostępnia operacji do formatowania napisów przez co ciężko łączy się ciągi znaków z liczbami i innymi danymi. Na szczęście do tworzenia sformatowanych ciągów znaków można użyć strumieni napisowych, wykorzystując wszystkie mechanizmy formatowania znane ze strumieni. string some_string = "some_string"; // C style /* char buffer[BUFFER_SIZE]; sprintf(buffer, "this is some text with integers %d, floats %f, hexes %x and strings %s\n", 9, 5.34, 0xF6, some_string.c_str()); */ // create string containing some text with integers, floats and strings ostringstream osstream; osstream << "this is some text with integers " << 9 << ", floats " << 5.34 << ", hexes " << hex << 0xF6 << " and strings " << some_string << endl; // print results cout << osstream.str(); ===== Parsowanie napisów ===== Podobnie strumieni napisowych można w wygodny sposób używać do wczytywania danych z ciągów znaków. string some_string; int some_number; float some_float; // C style /* char buffer[BUFFER_SIZE]; // contents: "some_text 9 5.56" sscanf(buffer, "%s %d %f", some_buffer, &some_nuber, &some_float); */ // parse text containing string, integer and float istringstream isstream("some_text 9 5.56"); isstream >> some_string >> some_number >> some_float; if(isstream.fail()) { // handle errors cout << "Error in parsing" << endl; } else { // print results cout << "Parsed items: " << endl << some_string << endl << some_number << endl << some_float << endl; } ===== Konwertowanie napisów ===== Strumieni napisów można także używać do konwersji w obydwie strony napisów i dowolnych innych typów. // C style /* some_number = atoi(buffer); // no error handling since 0 is valid integer */ // convert string to int with error handling and print it int some_number; istringstream isstream("123"); isstream >> some_number; if(isstream.fail()) { // handle errors cout << "Error while converting string to int" << endl; } else { // print results cout << "Converted string to integer: " << some_number << endl; } // convert int to string and print it ostringstream osstream; osstream << 9; cout << "Converted integer to string: "<< osstream.str() << endl; Takie podejście umożliwia wykrywanie błędów konwersji, jednak wymaga napisania dużej ilości kodu. Z tego powodu do konwertowania napisów zaleca się używanie klasy boost::lexical_cast, która opakowywuje strumienie napisowe i pozwala drastycznie skrócić powyższy zapis. try { int some_number = lexical_cast(some_string); } catch(bad_lexical_cast &) { // handle error } ===== Stringstream kontra Strstream ===== W bibliotece standardowej istnieje także rodzina klas strstream, która funkcjonalnością przypomina omawianą rodzinę stringstream, jednakże są to klasy przestarzałe i nie powinny być używane. Klasy stringstream są nową wersją strumieni napisowych, zgodną z obcenym standardem języka c++ i wolną od błędów projektowych klas strstream. ===== Przykładowy program ===== {{:strumienie_napisowe2.cpp|}} ===== Bibliografia ===== [[http://www.cplusplus.com/reference/iostream/stringstream/|www.cplusplus.com/reference/iostream/stringstream/]] [[http://www.cppreference.com/wiki/io/sstream/start|www.cppreference.com/wiki/io/sstream/start]]