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:
Nagłówek sstream dostarcza deklaracje trzech klas strumieni napisowych:
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 );
Biblioteka sstream dostarcza także definicji strumieni napisowych dla szerokich znaków, odpowiednio:
stringbuf* rdbuf ( ) const;
Funkcja zwraca wsaźnik na obiekt stringbuf w którym strumień przechowywuje dane.
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.
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.
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();
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; }
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<int>(some_string); } catch(bad_lexical_cast &) { // handle error }
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.