Canals de textos: sstream
Introducció
De la mateixa forma que es poden llegir dades amb cin
des del canal
estàndard d’entrada, es poden llegir seqüencialment dades que es troben
emmagatzemades en un text (string
) a través d’un objecte de tipus
istringstream
. Els objectes istringstream
ofereixen les mateixes
operacions de lectura que cin
, en particular els operadors >>
.
Anàlogament, enlloc d’escriure dades al canal estàndard de sortida amb cout
,
es poden escriure dades que s’aniràn emmagatzemant seqüencialment en un canal
a través d’un objecte de tipus ostringstream
. Els objectes ostringstream
ofereixen les mateixes operacions d’escriptura que cout
i cerr
, en
particular els operadors <<
. Un cop totes les dades han estat escrites al
canal, aquestes es poden consultar com a un únic text.
Per usar istringstream
s i ostringstream
s, cal fer un #include <sstream>
.
Els següents exemples expliquen i concreten el seu ús.
Exemple: Lectura d’una operació senzilla
Suposem que tenim una variable s
que té un valor que representa
una operació senzilla com ara "21.45 * 56.23"
i que en volem extreure els seus dos operands reals (21.45
i
56.23
) i el seu operador ('*'
). El que ens cal és llegir seqüencialment
de s
tres valors:
- el primer operand (de tipus real),
- l’operador (de tipus caràcter) i,
- el segon operand (de tipus real).
Per a fer-ho, declarem una variable iss
de tipus istringstream
que actui com a canal de les dades emmagatzemades en s
i després li apliquem
les tres lectures requerides:
string s = "21.45 * 56.23";
istringstream iss(s);
double operand1, operand2;
char operador;
iss >> operand1 >> operador >> operand2;
Així de senzill!
Exemple: Sumar nombres a cada línia
Considerem que tenim una fitxer compost de diferents linies i que cada línia conté una seqüència d’enters. Es vol obtenir la suma dels nombres de cada línia. Per exemple, si l’entrada és
56 12
1 2 3
9 1 1 14
la sortida hauria de ser
68
6
0
25
És clar que només llegint enters amb >>
des de cin
no podrem solucionar el problema,
perquè aquesta lectura salta els blancs (tant els separadors com els salts de línia)
i, per tant, no podem saber on acaba cada línia.
Una manera de resoldre-ho és utilitzant la lectura per línies. Recordeu que
getline(cin, s)
llegeix la següent línia de cin
i la desa en un string
s
. A més, aquesta funció retorna true
si ha pogut llegir la línia en
qüestió i retorna false
quan l’entrada ha acabat.
Per poder sumar tots els valors dins d’aquest text, es pot crear una nova variable
iss
de tipus istringstream
sobre cada línia llegida s
i aplicar
sobre iss
els operadors de lectura >>
habituals per obtenir-ne
tots els seus enters, tot acumulant-los.
El programa sencer és aquest:
#include <sstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
while (getline(cin, s)) {
istringstream iss(s);
int sum = 0;
int x;
while (iss >> x) sum += x;
cout << sum << endl;
}
}
Exemple: Convertir un enter en un text
Sovint es vol convertir un enter (int
) en un text (string
). La funció següent
ho fa tot utilitzant un ostringstream
:
string int_to_str(int x) {
ostringstream oss;
oss << x;
return oss.str();
}
Primer, es declara un objecte oss
de tipus ostringstream
que serà el canal on es
dipositaràn les dades. A continuació, s’escriu l’enter x
a oss
(de forma semblant
a com es faria amb cout
, però l’efecte és que ara no s’escriu res pel canal
de sortida, sinó que va a parar el canal oss
). Finalment, es recupera la informació
escrita en oss
com a text, utilitzant el seu mètode str()
.
💡 De fet, la funció estàndard to_string()
ja fa aquesta tasca per a tots els tipus
bàsics; vegeu-ne la documentació a
cppreference.
Exemple: Convertir entre una estructura i un text
Considerem que tenim un tipus de dades (de joguina!) per emmagatzemar la informació d’un pacient en alguna aplicació mèdica:
struct Pacient {
int dni;
string nom;
int edat;
double pes;
double alçada;
vector<int> id_metges; // identificadors dels metges que l'han tractat
};
La funció pacient_to_string()
següent utilitza un ostringstream
per codificar
les dades d’un pacient en un text fàcilment llegible tot separant cada camp
amb barres i agrupant entre parèntesis la llista dels identificadors dels seus
metges.
string pacient_to_string(const Pacient& p) {
ostringstream oss;
oss << p.dni << " / " << p.nom << " / " << p.edat << " / " << p.pes << " / " << p.alçada << " / ";
for (auto id : p.id_metges) {
oss << id << " ";
}
oss << "/";
return oss.str();
}
Per fer el pas invers, la funció string_to_pacient()
següent utilitza un istringstream
:
Pacient string_to_pacient(const string& s) {
Pacient p;
char c;
istringstream iss;
iss >> p.nom >> c >> p.nom >> c >> p.edat >> c >> p.pes >> c >> p.alçada >> c;
int id;
while (iss >> id) {
p.id_metges.push_back(id);
}
return p;
}
Fixeu-vos que el caràcter c
serveix per anar llegint les barres que separen els elements
i que, per simplicitat, no s’ha realitzat cap control d’errors.
Lliçons.jutge.org
Jordi Petit
Universitat Politècnica de Catalunya, 2023
Prohibit copiar. Tots els drets reservats.
No copy allowed. All rights reserved.