• 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

42 Warsaw Coding Academy
0 głosów
2,359 wizyt
pytanie zadane 29 lipca 2016 w C i C++ przez Sebastian Fojcik Nałogowiec (43,040 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,040 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,040 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ź 749 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ź 299 wizyt
pytanie zadane 11 marca 2017 w C i C++ przez Meltern Początkujący (440 p.)
0 głosów
2 odpowiedzi 587 wizyt
pytanie zadane 11 października 2016 w C i C++ przez JanuszSTW Początkujący (480 p.)

93,389 zapytań

142,386 odpowiedzi

322,548 komentarzy

62,749 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

VMware Cloud PRO - przenieś swoją infrastrukturę IT do chmury
...