Что именно делает Stringstream?

107

Я пытаюсь выучить C ++ со вчерашнего дня и использую этот документ: http://www.cplusplus.com/files/tutorial.pdf (стр. 32). Я нашел код в документе и запустил его. Я попытался ввести 5,5 рупий для цены и целое число для количества, и результат был 0. Я попытался ввести 5,5 и 6, и результат был правильным.

// 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; 
}

Вопрос: Что именно делает команда mystring? Цитата из документа:

"В этом примере мы получаем числовые значения из стандартного ввода косвенно. Вместо извлечения числовых значений непосредственно из стандартного ввода мы получаем строки из стандартного ввода (cin) в строковый объект (mystr), а затем извлекаем целое число значения из этой строки в переменную типа int (количество) ".

У меня сложилось впечатление, что функция будет принимать неотъемлемую часть строки и использовать ее в качестве входных данных.

(Я точно не знаю, как здесь задать вопрос. Я тоже новичок в программировании) Спасибо.

Джекнад
источник
19
Этот пример довольно странный, я никогда не видел, чтобы он stringstreamиспользовался таким образом. Обычно я загружаю строку, конвертирую ее, а затем извлекаю по частям, однако это явно не имеет здесь большого преимущества, потому что cin это уже входной поток ... Так cin >> price >> quantity;было бы намного проще. Это будет хорошей причиной НЕ использовать учебные материалы cplusplus.com.
Bartek Banachewicz
6
Забавно, что этот урок был моим первым знакомством с C ++. Оглядываясь назад, можно сказать, что это довольно плохо и неполно. Я бы посоветовал вместо этого хорошую книгу .
jrok
@BartekBanachewicz Может быть, им просто нужно было придумать пример, чтобы показать, как stringstreamработает. Это странно, возможно, даже плохо =) Но это показывает, что вы можете рассматривать строку как поток.
luk32
Если это не введение в более сложные способы использования, stringstreamто это определенно неверный пример. И даже если это так, то надо писать иначе.
j_kubik
1
@trojansdestroy Вы не можете понять stringstream, не понимая всех примитивов, на которых он основан, поэтому я не понимаю, как чтение учебника помогает в этом отношении.
Bartek Banachewicz

Ответы:

163

Иногда очень удобно использовать stringstream для преобразования между строками и другими числовыми типами. Использование stringstreamаналогично использованию iostream, поэтому изучать его не составляет труда .

Stringstreams можно использовать как для чтения строк, так и для записи данных в строки. В основном он работает со строковым буфером, но без реального канала ввода-вывода.

Основные функции-члены класса stringstream:

  • str(), который возвращает содержимое своего буфера строкового типа.

  • str(string), который устанавливает содержимое буфера в строковый аргумент.

Вот пример того, как использовать строковые потоки.

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

Результат есть dec: 15 hex: f.

istringstream имеет более или менее такое же употребление.

Подводя итог, можно сказать, что stringstream - это удобный способ манипулировать строками как независимым устройством ввода-вывода .

К вашему сведению, отношения наследования между классами:

классы строкового потока

Ричард.г
источник
19

Чтобы ответить на вопрос. stringstreamв основном позволяет обращаться с stringобъектом как с a streamи использовать с ним все streamфункции и операторы.

Я видел, что он использовался в основном для форматированного вывода / ввода.

Хорошим примером может быть c++реализация преобразования числа в объект потока.

Возможный пример:

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;
};

Может быть, это немного сложно, но довольно сложно. Вы создаете stringstreamобъект ss, изменяете его флаги, вставляете в него число operator<<и извлекаете его через str(). Думаю, это operator>>можно было бы использовать.

Также в этом примере stringбуфер скрыт и не используется явно. Но это был бы слишком длинный пост, чтобы писать обо всех возможных аспектах и ​​сценариях использования.

Примечание: я, вероятно, украл его у кого-то на SO и уточнил, но у меня нет оригинального автора.

luk32
источник
3
Примечание: использование retнеобязательно, можно было бы написать return ss.str();.
Matthieu M.
@MatthieuM. Думаю, я не был уверен, сработает ли RVO, если он был написан таким образом, или если объект, возвращенный ss.str (), переживет точку выхода. Таким образом я знаю, что делаю копию, и RVO будет работать. Но вы, скорее всего, правы.
luk32
На самом деле существует 2 формы RVO: URVO (для безымянных, то есть временных) и NRVO (для именованных); большинство компиляторов реализуют RVO, но некоторые ограничивают его только URVO (в зависимости от параметров сборки). В целом, однако, есть множество других факторов, которые следует учитывать, поэтому вам следует просто писать максимально чистый код и не слишком беспокоиться о том, сработает ли RVO.
Матье М.
NRVO является обычным явлением (как и URVO), однако это не проблема из-за конструкторов перемещения.
Rapptz
18

Из C ++ Primer :

Тип istringstream читает строку , ostringstream записывает строку , а stringstream читает и записывает строку .

Я сталкивался с некоторыми случаями, когда использовать stringstream одновременно и удобно, и кратко .

Случай 1

Это от одного из решений для этой проблемы leetcode . Он демонстрирует очень подходящий случай, когда использование stringstream эффективно и кратко.

Предположим, что aи b- комплексные числа, выраженные в строковом формате, мы хотим получить результат умножения, aа bтакже в строковом формате. Код выглядит следующим образом:

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() 

случай 2

Это также из-за проблемы leetcode, которая требует от вас упрощения заданной строки пути, одно из решений, использующих stringstream, является наиболее элегантным, что я видел:

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; 
 }

Без использования строкового потока было бы сложно написать такой краткий код.

Jdhao
источник
2

Вы ввели буквенно-цифровые и целые числа, разделенные пробелами mystr.

Затем вы попытались преобразовать первый токен (разделенный пробелами) в файл int.

Первым токеном был RS, который не удалось преобразовать в int, оставив ноль для myprice, и все мы знаем, что дает ноль, умноженное на что-либо.

Когда вы во второй раз вводили только значения int, все работало так, как вы ожидали.

Это был ложный RS, который привел к сбою вашего кода.

Илокос Джо
источник