• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[C++] Dane nie są wyciągane ze strumienia stringstream

VPS Starter Arubacloud
0 głosów
1,902 wizyt
pytanie zadane 29 lipca 2016 w C i C++ przez Sebastian Fojcik Nałogowiec (43,020 p.)

Witam, mam problem ze strumieniami stringstream, które nie zachowują się tak, jak powinny (według książek i przykładów z internetu). Korzystam z Visual Studio 2015. Weźmy taki przykład:

string zmienna;
stringstream ss;
ss << "Napis";
cout << ss.str() << endl; // 1 Wypisze: Napis
ss >> zmienna;
cout << ss.str() << endl; // 2 Wypisze: Napis (!)

Wprowadzam do strumienia dane i wypisanie zawartości strumienia pokazuje, że rzeczywiście znajduje się tam "Napis". Jednak dziwne rzeczy dzieją się po wyprowadzeniu danych ze strumienia i zapisania ich do zmiennej.

Zmienna odczytuje dane, a strumień przesuwa wskaźnik odczytu dalej. Z tego co mi się wydaje, to zachowanie powinno być zupełnie inne. Strumień powinien wyrzucić dane zaraz po wykonaniu:

ss >> zmienna;

Tak pokazują m.in. przykłady z książki Symfonia. Po odczytaniu wszystkiego, wywołanie ss.str(); zwracało pusty string i tak samo powinno być w punkcie 2. Czy tylko biblioteki z Visual Studio mają takie anomalie? Zmieniono specyfikację tych strumieni, czy może ja coś źle robię?

1 odpowiedź

+1 głos
odpowiedź 29 lipca 2016 przez MetGang Nałogowiec (34,360 p.)
edycja 29 lipca 2016 przez MetGang
Przetestowałem u siebie na GCC - zachowanie takie samo i jak dla mnie poprawne. Jest to strumień danych, a operator >> odczytuje dane oddzielone białymi znakami i wpisuje je do danej zmiennej, jednocześnie przesuwając wskaźnik odczytu dalej. Funkcja str() natomiast zwraca cały bufor jaki strumień przechowuje, więc nie dziwię się, że zwraca cały napis. Za pomocą ss.clear() i ss.str("") możesz też wyczyścić strumień.

Z jakiego roku książka i jaki standard opisuje?
komentarz 29 lipca 2016 przez Sebastian Fojcik Nałogowiec (43,020 p.)

Książka jest trzecim wydaniem rozszerzonym i opisuje standard C++ ISO/IEC 14882. Niestety nie wiadomo, który dokładnie rok, bo była ona poprawiana na przestrzeni lat.

Jeśli ktoś posiada Symfonię wydanie trzecie, to może sprawdzić przykładowy program ze strony 1034. Tak czy inaczej jest to całkiem istotna zmiana w zachowaniu strumienia. Strumień stringstream powinien jednak działać inaczej. Operator >> powinien wyciągać dane ze strumienia, a nie je tylko odczytywać. Dlaczego? Bo po odczytaniu danych nie można już nic zapisać (bez ręcznego ustawiania wskaźnika) co się całkowicie sprzecza z sensem istnienia strumienia stringstream, który ma czytać i zapisywać jednocześnie,

Przykładowy kod, który nie działa (co jest wg. mnie bezsensowne)

string zmienna;
string zmienna2;
stringstream ss;

ss << "Napis1";
ss >> zmienna;
// Od tego momentu strumień nie nadaje się do zapisu.
// Flaga End-Of-File ustawiona. Dziękujemy za współpracę.

ss << "Napis2"; // nic się nie dzieje
ss >> zmienna2; // string pusty :/

Wskaźnik odczytu jest nadal ustawiony na koniec "Napis1", a przez to strumień jest bezużyteczny. W Symfonii C++ Grębosz wielokrotnie wywoływał operatory << oraz >> raz za razem po sobie. Strumień raz był pusty (po wyciągnięciu danych >>) a raz się zapełniał (<<). Oczywiście można sobie z tym poradzić:

string zmienna;
string zmienna2;
stringstream ss;

ss << "Napis1";
ss >> zmienna;

ss.clear();

ss << "Napis2"; // ss.str() zwróci: Napis1Napis2
ss >> zmienna2; // zamienna2 zawiera Napis2

Ale takie zachowanie nie ma sensu. "Napis1" nie jest mi już do niczego potrzebny po odczytaniu. Marnuje pamięć. Obsługa strumienia zapisu-odczytu stała się teraz mniej intuicyjna. Trzeba ręcznie kasować flagi błędu, czyścić bufor, ustawiać wskaźnik odczytu/zapisu na początek...

komentarz 10 lutego 2020 przez niezalogowany

Sorki że odświeżam temat

ja bym chciał skorzystać z napisu ale chyba czegoś nie kumam

std::stringstream ss("jakis sobie str");
std:: string strwczytajzestrumiena;
ss>>strwczytajzestrumiena;
ss.clear(ss.rdstate()&~ss.eofbit);
ss.seekp(0,ss.beg);

//ale nie jest pusty bo
 std::cout<<ss.str()<<std::endl; //jakiś sobie string
komentarz 10 lutego 2020 przez Sebastian Fojcik Nałogowiec (43,020 p.)

Tak jak napisał MetGang 3 lata temu, operator wyciągania danych ze strumienia ">>" jedynie przesuwa wskaźnik odczytu w danym strumieniu, ale nie "wyciąga" z niego danych. Natomiast funkcja "str()" zwraca całą zawartość strumienia (odczytaną lub nie).

Po  ponad 3 latach od napisania tego pytania na forum nadal nie rozumiem dlaczego stringstream działa w taki sposób. Spojrzałem nawet przed chwilą do dokumentacji operatora >> oraz funkcji str(). Nie piszą tam wprost jak operator odczytu wpływa na zwartość strumienia (albo ja nie widzę).

Jedyne obejście tego problemu jakie znalazłem, które nawiązuje do tego tematu jest przykład z tej strony:

https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt

Wklejam go poniżej:

#include <iostream>
#include <iomanip>
#include <sstream>
 
int main()
{
    std::string input = "41 3.14 false hello world";
    std::istringstream stream(input);
    int n;
    double f;
    bool b;
 
    stream >> n >> f >> std::boolalpha >> b;
    std::cout << "n = " << n << '\n'
              << "f = " << f << '\n'
              << "b = " << std::boolalpha << b << '\n';
 
    // extract the rest using the streambuf overload
    stream >> std::cout.rdbuf();
    std::cout << '\n';
}

Wyjście:

n = 41
f = 3.14
b = false
hello world

Najważniejsza jest linijka: stream >> std::cout.rdbuf();. To tutaj jest pokazany przykład wypisywania "tego co pozostało" w strumieniu.

Inny ciekawy przykład można znaleźć tutaj: https://en.cppreference.com/w/cpp/io/basic_stringstream/str

Ponownie wklejam poniżej:

#include <sstream>
#include <iostream>
int main()
{
    int n;
 
    std::istringstream in;  // could also use in("1 2")
    in.str("1 2");
    in >> n;
    std::cout << "after reading the first int from \"1 2\", the int is "
              << n << ", str() = \"" << in.str() << "\"\n";
 
    std::ostringstream out("1 2");
    out << 3;
    std::cout << "after writing the int '3' to output stream \"1 2\""
              << ", str() = \"" << out.str() << "\"\n";
 
    std::ostringstream ate("1 2", std::ios_base::ate);
    ate << 3;
    std::cout << "after writing the int '3' to append stream \"1 2\""
              << ", str() = \"" << ate.str() << "\"\n";
}

Wyjście:

after reading the first int from "1 2", the int is 1, str() = "1 2"
after writing the int '3' to output stream "1 2", str() = "3 2"
after writing the int '3' to append stream "1 2", str() = "1 23"

Zauważ, że w drugim przypadku (strumień ostringstream bez flagi std::ios_base::ate) do strumienia wpisaliśmy "3" na początek i jedynka "1", która się tam znajdowała została nadpisana.

Strumienie w C++ to bardzo rozbudowany zestaw funkcji i mówiąc szczerze, to bałbym się z nich korzystać w większym projekcie. To co moim zdaniem pokazuje, że stringstream jest klasą co najmniej źle zaprojektowaną, jest mnogość wewnętrznych stanów, wiele różnych flag z błędami, niejasny wpływ wywoływanych funkcji na stan wewnętrzny obiektu i wiele więcej.

komentarz 10 lutego 2020 przez niezalogowany
edycja 10 lutego 2020

edit2:: edytowałem bo straszne bzdury były napisane.

ale po paru godzinach i tak nie kumam dlaczego to 

http://www.cplusplus.com/reference/streambuf/streambuf/in_avail/

zwraca 0;

a ss.str(ss.str()) jest ok

edit 3 :: Dzięki za kumamełm nie to seek użyłem 

Podobne pytania

0 głosów
1 odpowiedź 460 wizyt
pytanie zadane 25 stycznia 2019 w C i C++ przez Ryszard Kałuziński Początkujący (280 p.)
0 głosów
1 odpowiedź 179 wizyt
pytanie zadane 11 marca 2017 w C i C++ przez Meltern Początkujący (440 p.)
0 głosów
2 odpowiedzi 319 wizyt
pytanie zadane 11 października 2016 w C i C++ przez JanuszSTW Początkujący (480 p.)

92,455 zapytań

141,263 odpowiedzi

319,099 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...