Що саме робить стрингстрім?


107

Я намагаюся вивчити C ++ з вчорашнього дня і використовую цей документ: http://www.cplusplus.com/files/tutorial.pdf (стор. 32). Я знайшов код у документі і запустив його. Я спробував ввести Rs 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 (кількість). "

Моє враження було, що функція буде приймати невід'ємну частину рядка і використовуватиме її як вхід.

(Я точно не знаю, як тут поставити запитання. Я теж новачок у програмуванні) Дякую.


18
Цей приклад начебто дивний, я ніколи не бачив, stringstreamщоб використовувався таким чином. Я зазвичай навантажує лінію, перетворити його , а потім витягти по частинах, проте це , очевидно , має невелику перевагу тут , тому що cin це вхідний потік вже ... Так cin >> price >> quantity;було б набагато простіше. Це було б вагомою причиною НЕ використовувати підручники cplusplus.com.
Bartek Banachewicz

6
Смішно, що цей підручник був моїм першим впливом на C ++. Заднім числом це досить бідно і неповно. Я б запропонував гарну книгу замість цього.
jrok

@BartekBanachewicz Можливо, їм просто потрібно було придумати приклад, щоб показати, як stringstreamпрацює. Це химерний, мабуть, навіть поганий =) Але це показує, що ви можете ставитися до рядка як до потоку.
luk32

Якщо це не вступ до більш досконалого використання, stringstreamто це, безумовно, неправильний приклад. І навіть якщо це так, то слід писати інакше.
j_kubik

1
@trojansdestroy Ви не можете зрозуміти потоковий потік, не розуміючи всіх примітивів, на яких він заснований, тому я не бачу, як читання підручника допомагає в цьому плані.
Bartek Banachewicz

Відповіді:


163

Іноді для конвертації між рядками та іншими числовими типами дуже зручно використовувати стрингстрім. Використання stringstreamподібне до використання iostream, тому вчитися не є тягарем.

Строкові потоки можна використовувати як для читання рядків, так і для запису даних у рядки. Він в основному функціонує з буфером рядків, але без реального каналу вводу / виводу.

Основними функціями членів класу stringstream є:

  • str(), який повертає вміст свого буфера в рядковому типі.

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

Ось приклад того, як використовувати потокові потоки.

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

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

istringstream має більш-менш однакове використання.

Підводячи підсумок, streamstream - це зручний спосіб маніпулювання рядками, як незалежний пристрій вводу / виводу .

FYI, відносини спадкування між класами є:

класи потокових рядків


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 та вдосконалив, але в мене немає оригінального автора.


3
Примітка: використання retнепотрібне, можна було б написати return ss.str();.
Матьє М.

@MatthieuM. Думаю, я не був впевнений, чи буде RVO запускати, якби це було написано, або якщо об'єкт, повернутий ss.str (), вижив би точку виходу. Таким чином я знаю, що роблю копію, і RVO запрацює. Але ти, мабуть, маєш рацію.
luk32

Власне, існує 2 форми RVO: URVO (для неназваних, тобто тимчасових) та NRVO (для названих); більшість компіляторів реалізує RVO, але деякі обмежують його лише URVO (залежно від варіантів збірки). Взагалі, однак, є багато інших факторів, які слід враховувати, тому вам слід просто написати найчистіший можливий код і не надто переживати про те, чи вдариться RVO.
Маттьє М.

NRVO є загальним (як і URVO), однак це не проблема через конструктори, що рухаються.
Rapptz

18

Від C ++ Primer :

Тип istringstream читає рядок , ostringstream записує рядок , а stringstream читає і записує рядок .

Я натрапляю на деякі випадки, коли використовувати стринг-стрим зручно і стисло .

випадок 1

Це від одного з рішень для цієї проблеми leetcode . Це демонструє дуже підходящий випадок, коли використання потокової лінії є ефективним та стислим.

Припустимо, 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; 
 }

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


2

Ви ввели буквено-цифровий і int, пропущений з розділеними в mystr.

Потім ви спробували перетворити перший маркер (порожній відмежований) у int.

Першим жетоном був RS, який не зміг перетворити int, залишивши нуль для моєї ціни, і всі ми знаємо, що нуль разів приносить щось.

Коли ви лише вдруге ввели значення int, все працювало так, як ви очікували.

Саме помилковий RS призвів до відмови вашого коду.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.