Co dokładnie robi stringstream?


107

Od wczoraj staram się uczyć C ++ i używam tego dokumentu: http://www.cplusplus.com/files/tutorial.pdf (strona 32). Znalazłem kod w dokumencie i uruchomiłem go. Próbowałem wprowadzić Rs 5,5 dla ceny i liczbę całkowitą dla ilości i na wyjściu było 0. Próbowałem wprowadzić 5,5 i 6 i wyjście było poprawne.

// stringstreams
#include <iostream> 
#include <string> 
#include <sstream> 

using namespace std; 

int main () 
{ 
  string mystr; 
  float price = 0; 
  int quantity = 0; 

  cout << "Enter price: "; 
  getline (cin,mystr); 
  stringstream(mystr) >> price; 
  cout << "Enter quantity: "; 
  getline (cin,mystr); 
  stringstream(mystr) >> quantity; 
  cout << "Total price: " << price*quantity << endl; 
  return 0; 
}

Pytanie: Co dokładnie robi polecenie mystring? Cytat z dokumentu:

„W tym przykładzie pośrednio pobieramy wartości liczbowe ze standardowego wejścia. Zamiast wyodrębniać wartości liczbowe bezpośrednio ze standardowego wejścia, pobieramy wiersze ze standardowego wejścia (cin) do obiektu łańcuchowego (mystr), a następnie wyodrębniamy liczbę całkowitą wartości z tego ciągu do zmiennej typu int (ilość). "

Odniosłem wrażenie, że funkcja weźmie integralną część łańcucha i użyje jej jako danych wejściowych.

(Nie wiem dokładnie, jak zadać tutaj pytanie. Jestem też nowy w programowaniu) Dziękuję.


19
Ten przykład jest trochę dziwny, nigdy nie widziałem, żeby był stringstreamużywany w ten sposób. Zwykle ładuję linię, konwertuję ją, a następnie wyodrębniam na części, jednak ma to oczywiście niewielką zaletę, ponieważ cin jest już strumieniem wejściowym ... Więc cin >> price >> quantity;byłoby o wiele prostsze. To byłby dobry powód, aby NIE używać samouczków cplusplus.com.
Bartek Banachewicz

6
Zabawne, że ten samouczek był moim pierwszym kontaktem z C ++. Z perspektywy czasu jest dość ubogi i niekompletny. Zamiast tego zaproponowałbym dobrą książkę .
jrok

@BartekBanachewicz Może po prostu trzeba było podać przykład, żeby pokazać, jak stringstreamdziała. Jest to dziwaczne, prawdopodobnie nawet złe =) Ale pokazuje, że możesz traktować ciąg jako strumień.
luk32

Jeśli nie jest to wprowadzenie do bardziej zaawansowanych zastosowań, stringstreamto zdecydowanie jest to zły przykład. A nawet jeśli tak, to powinno być inaczej napisane.
j_kubik,

1
@trojansdestroy Nie możesz zrozumieć stringstream bez zrozumienia wszystkich prymitywów, na których jest oparty, więc nie widzę, jak czytanie samouczka pomaga w tym względzie.
Bartek Banachewicz

Odpowiedzi:


163

Czasami bardzo wygodne jest użycie stringstream do konwersji między łańcuchami i innymi typami liczbowymi. Używanie stringstreamjest podobne do używania iostream, więc nauka nie jest ciężarem.

Stringstreams może służyć zarówno do odczytywania ciągów, jak i do zapisywania danych w łańcuchach. Działa głównie z buforem łańcuchowym, ale bez prawdziwego kanału I / O.

Podstawowe funkcje składowe klasy stringstream to

  • str(), która zwraca zawartość swojego bufora w postaci łańcucha.

  • str(string), które ustawiają zawartość buforu na argument ciągu.

Oto przykład użycia strumieni ciągów.

ostringstream os;
os << "dec: " << 15 << " hex: " << std::hex << 15 << endl;
cout << os.str() << endl;

Wynik jest dec: 15 hex: f.

istringstream ma mniej więcej takie samo zastosowanie.

Podsumowując, stringstream to wygodny sposób na manipulowanie łańcuchami jak niezależne urządzenie we / wy .

FYI, relacje dziedziczenia między klasami są następujące:

klasy strumieniowe


19

Aby odpowiedzieć na pytanie. stringstreamw zasadzie pozwala traktować stringobiekt jak a streami używać na nim wszystkich streamfunkcji i operatorów.

Widziałem, że jest używany głównie do sformatowanych danych wyjściowych / wejściowych.

Dobrym przykładem może być c++implementacja konwersji liczby na obiekt strumieniowy.

Możliwy przykład:

template <class T>
string num2str(const T& num, unsigned int prec = 12) {
    string ret;
    stringstream ss;
    ios_base::fmtflags ff = ss.flags();
    ff |= ios_base::floatfield;
    ff |= ios_base::fixed;
    ss.flags(ff);
    ss.precision(prec);
    ss << num;
    ret = ss.str();
    return ret;
};

Może jest to trochę skomplikowane, ale jest dość złożone. Tworzysz stringstreamobiekt ss, modyfikujesz jego flagi, umieszczasz w nim liczbę operator<<i wyodrębniasz za pomocą str(). Myślę, że operator>>można to wykorzystać.

Również w tym przykładzie stringbufor jest ukryty i nie jest używany jawnie. Jednak wpis o każdym możliwym aspekcie i przypadku użycia byłby zbyt długi.

Uwaga: prawdopodobnie ukradłem go komuś na SO i dopracowałem, ale nie odnotowałem oryginalnego autora.


3
Uwaga: użycie retjest niepotrzebne, można by napisać return ss.str();.
Matthieu M.,

@MatthieuM. Myślę, że nie byłem pewien, czy RVO włączy się, jeśli zostanie napisane w ten sposób, lub jeśli obiekt zwrócony przez ss.str () przetrwa punkt wyjścia. W ten sposób wiem, że robię kopię, a RVO będzie działać. Ale najprawdopodobniej masz rację.
luk32

Właściwie istnieją 2 formy RVO: URVO (dla nienazwanego, czyli tymczasowego) i NRVO (dla nazwanego); większość kompilatorów implementuje RVO, ale niektóre ograniczają go tylko do URVO (w zależności od opcji kompilacji). Ogólnie jednak jest wiele innych czynników, które należy wziąć pod uwagę, więc powinieneś po prostu napisać jak najczystszy kod i nie martwić się zbytnio o to, czy RVO zadziała.
Matthieu M.

NRVO jest powszechne (podobnie jak URVO), ale nie stanowi problemu z powodu konstruktorów przenoszenia.
Rapptz,

18

Z C ++ Primer :

Istringstream typ odczytuje ciąg , ostringstream zapisuje ciąg , a stringstream odczytuje i zapisuje ciąg .

Spotykam się z przypadkami, w których użycie stringstreamu jest zarówno wygodne, jak i zwięzłe .

przypadek 1

To właśnie z jednym z rozwiązań dla tego problemu leetcode . Pokazuje bardzo odpowiedni przypadek, w którym użycie stringstream jest wydajne i zwięzłe.

Załóżmy ai bsą liczbami zespolonymi wyrażone w formacie ciągu chcemy uzyskać wynik mnożenia aa btakże w formacie strun. Kod wygląda następująco:

string a = "1+2i", b = "1+3i";
istringstream sa(a), sb(b);
ostringstream out;

int ra, ia, rb, ib;
char buff;
// only read integer values to get the real and imaginary part of 
// of the original complex number
sa >> ra >> buff >> ia >> buff;
sb >> rb >> buff >> ib >> buff;

out << ra*rb-ia*ib << '+' << ra*ib+ia*rb << 'i';

// final result in string format
string result = out.str() 

przypadek 2

Wynika to również z problemu z kodem leetcode, który wymaga uproszczenia podanego ciągu ścieżki, jedno z rozwiązań wykorzystujących stringstream jest najbardziej eleganckie, jakie widziałem:

string simplifyPath(string path) {
    string res, tmp;
    vector<string> stk;
    stringstream ss(path);
    while(getline(ss,tmp,'/')) {
        if (tmp == "" or tmp == ".") continue;
        if (tmp == ".." and !stk.empty()) stk.pop_back();
        else if (tmp != "..") stk.push_back(tmp);
    }
    for(auto str : stk) res += "/"+str;
    return res.empty() ? "/" : res; 
 }

Trudno byłoby napisać taki zwięzły kod bez użycia stringstream.


2

Wpisałeś alfanumeryczny i int, oddzielony spacją mystr.

Następnie próbowałeś przekonwertować pierwszy token (rozdzielany spacjami) na plik int.

Pierwszym tokenem był RS, którego nie udało się przekonwertować na int, pozostawiając zero dla myprice, a wszyscy wiemy, ile daje zero razy cokolwiek.

Gdy wprowadziłeś tylko wartości int za drugim razem, wszystko działało zgodnie z oczekiwaniami.

To fałszywy RS spowodował awarię twojego kodu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.