¿Qué hace exactamente Stringstream?

107

Estoy tratando de aprender C ++ desde ayer y estoy usando este documento: http://www.cplusplus.com/files/tutorial.pdf (página 32). Encontré un código en el documento y lo ejecuté. Intenté ingresar Rs 5.5 para el precio y un número entero para la cantidad y la salida fue 0. Intenté ingresar 5.5 y 6 y la salida fue correcta.

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

Pregunta: ¿Qué hace exactamente el comando mystring? Citando del documento:

"En este ejemplo, adquirimos valores numéricos de la entrada estándar de forma indirecta. En lugar de extraer valores numéricos directamente de la entrada estándar, obtenemos líneas de la entrada estándar (cin) en un objeto de cadena (mystr) y luego extraemos el número entero valores de esta cadena en una variable de tipo int (cantidad) ".

Mi impresión fue que la función tomará la parte integral de una cadena y la usará como entrada.

(No sé exactamente cómo hacer una pregunta aquí. También soy nuevo en la programación) Gracias.

Jacknad
fuente
18
Este ejemplo es un poco extraño, nunca lo he visto stringstreamusado de esa manera. Por lo general, cargo la línea, la convierto y luego la extraigo por partes, sin embargo, esto obviamente tiene poca ventaja aquí porque ya cin es un flujo de entrada ... Así cin >> price >> quantity;que sería mucho más simple. Esa sería una buena razón para NO usar los tutoriales de cplusplus.com.
Bartek Banachewicz
6
Es curioso que ese tutorial fuera mi primera exposición a C ++. En retrospectiva, es bastante pobre e incompleto. En su lugar, sugiero un buen libro .
jrok
@BartekBanachewicz Tal vez solo necesitaban dar un ejemplo para mostrar cómo stringstreamfunciona. Es extraño, probablemente incluso malo =) Pero muestra que puedes tratar la cadena como una secuencia.
luk32
Si no es una introducción a usos más avanzados stringstream, definitivamente es un ejemplo incorrecto. E incluso si lo es, debería escribirse de manera diferente.
j_kubik
1
@trojansdestroy No se puede entender el flujo de cadenas sin comprender todas las primitivas en las que se basa, así que no veo cómo la lectura de un tutorial ayude en ese sentido.
Bartek Banachewicz

Respuestas:

163

A veces es muy conveniente usar stringstream para convertir entre cadenas y otros tipos numéricos. El uso de stringstreames similar al uso de iostream, por lo que no es una carga para aprender.

Stringstreams se puede utilizar tanto para leer cadenas como para escribir datos en cadenas. Funciona principalmente con un búfer de cadena, pero sin un canal de E / S real.

Las funciones miembro básicas de la clase stringstream son

  • str(), que devuelve el contenido de su búfer en tipo de cadena.

  • str(string), que establece el contenido del búfer en el argumento de cadena.

A continuación, se muestra un ejemplo de cómo utilizar secuencias de cadenas.

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

El resultado es dec: 15 hex: f.

istringstream es de más o menos el mismo uso.

En resumen, stringstream es una forma conveniente de manipular cadenas como un dispositivo de E / S independiente .

Para su información, las relaciones de herencia entre las clases son:

clases de secuencia de cadenas

richard.g
fuente
19

Para responder a la pregunta. stringstreambásicamente le permite tratar un stringobjeto como a stream, y usar todas las streamfunciones y operadores en él.

Lo vi utilizado principalmente para la bondad de entrada / salida formateada.

Un buen ejemplo sería la c++implementación de convertir un número en un objeto de flujo.

Posible ejemplo:

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

Quizás sea un poco complicado pero es bastante complejo. Usted crea un stringstreamobjeto ss, modifica sus banderas, le pone un número operator<<y lo extrae mediante str(). Supongooperator>> podría usarse.

También en este ejemplo, el stringbúfer está oculto y no se usa explícitamente. Pero sería una publicación demasiado larga para escribir sobre todos los aspectos y casos de uso posibles.

Nota: Probablemente se lo robé a alguien en SO y lo refiné, pero no tengo el autor original anotado.

luk32
fuente
3
Nota: el uso de retes innecesario, se podría escribir return ss.str();.
Matthieu M.
@MatthieuM. Creo que no estaba seguro de si RVO se activaría si estuviera escrito así, o si el objeto devuelto por ss.str () sobreviviría al punto de salida. De esta manera, sé que estoy haciendo una copia y que RVO funcionará. Pero probablemente tengas razón.
luk32
En realidad, hay 2 formas de RVO: URVO (para sin nombre, es decir, temporales) y NRVO (para con nombre); la mayoría de los compiladores implementan RVO, pero algunos lo restringen solo a URVO (dependiendo de las opciones de compilación). Sin embargo, en general, hay muchos otros factores a tener en cuenta, por lo que debe escribir el código más limpio posible y no preocuparse demasiado por si RVO se activará.
Matthieu M.
NRVO es común (al igual que URVO), sin embargo, no es un problema debido a los constructores de movimientos.
Rapptz
18

Desde C ++ Primer :

El tipo istringstream lee una cadena , ostringstream escribe una cadena y stringstream lee y escribe la cadena .

Encontré algunos casos en los que es conveniente y conciso usar stringstream .

caso 1

Es de una de las soluciones para este problema de código de lectura . Demuestra un caso muy adecuado donde el uso de stringstream es eficiente y conciso.

Supongamos que ay bson números complejos expresados ​​en formato de cadena, queremos obtener el resultado de la multiplicación de ay btambién en formato de cadena. El código es el siguiente:

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

caso 2

También es de un problema de código de lectura que requiere que simplifique la cadena de ruta dada, una de las soluciones que usa stringstream es la más elegante que he visto:

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

Sin el uso de stringstream, sería difícil escribir un código tan conciso.

jdhao
fuente
2

Ingresó alfanumérico e int, en blanco delimitado en mystr.

Luego intentó convertir el primer token (delimitado por espacios en blanco) en un archivo int.

El primer token fue RS, que no se pudo convertir int, dejando un cero para myprice, y todos sabemos lo que rinde cero veces cualquier cosa.

Cuando solo ingresó valores int la segunda vez, todo funcionó como esperaba.

Fue el RS espurio lo que hizo que tu código fallara.

ilocos joe
fuente