Menu

String stream in C++: parsing e costruzione con stringstream

Come usare std::stringstream, istringstream e ostringstream per analizzare testo, dividere stringhe sugli spazi, convertire tra stringhe e numeri e costruire stringhe formattate in memoria.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Uno stream che vive in una stringa

Conosci già cin e cout, stream collegati alla tastiera e allo schermo. Uno string stream è la stessa idea, solo che i dati vivono in una std::string in memoria. Ci scrivi con << e ne estrai valori con >>, esattamente come con gli stream della console, ma nulla tocca il terminale.

Quest'unica idea risolve due problemi di tutti i giorni: analizzare un testo scomponendolo in valori tipizzati e costruire una stringa formattata a partire da pezzi eterogenei. Tutto si trova nell'header <sstream> ed esiste in tre varianti:

  • istringstream: sola lettura, per analizzare l'input.
  • ostringstream: sola scrittura, per costruire l'output.
  • stringstream: entrambe le direzioni.

ss.str() restituisce tutto ciò che è stato scritto finora come una semplice string. Gli operatori << hanno eseguito la stessa formattazione da int a testo che fa cout: hai solo catturato il risultato invece di stamparlo.

Parsing: estrarre valori tipizzati

La vera potenza emerge con >>. Dai del testo a uno stream e l'estrazione converte ogni token nel tipo che chiedi, saltando gli spazi tra un token e l'altro. È il modo pulito per scomporre una riga in più campi tipizzati:

Ogni >> legge fino al prossimo spazio e converte: "Ada" in una string, "36" in un int, "5.5" in un double. Nota l'uso di istringstream: il tipo di sola lettura rende chiaro che stai soltanto analizzando.

Convertire stringhe in numeri (in sicurezza)

Un compito frequente è trasformare una singola stringa come "42" in un int. Uno stringstream lo fa e ti dice se il testo era davvero un numero valido, cosa che atoi non fa mai:

Qui ss >> value legge 123, si ferma alla a e riesce: quindi, se ti interessa, valida sempre l'intera stringa. Un controllo robusto consiste nel leggere il numero e poi verificare che non resti nulla di significativo. Per le conversioni semplici di un solo valore, std::stoi, std::stod e simili sono più brevi; usa stringstream quando una stringa contiene più valori di tipo diverso.

Dividere una stringa su un delimitatore

Dividere un testo è il compito sugli string stream più cercato su Google. Combina uno stream con std::getline, che sa leggere fino a qualsiasi carattere delimitatore, non solo fino a un ritorno a capo:

Lo schema while (getline(...)) cicla finché lo stream non si esaurisce, perché getline restituisce lo stream e lo stream vale false quando non resta più nulla. Ogni token viene aggiunto a un vector, il posto naturale per raccogliere un numero variabile di pezzi. Togli il terzo argomento e dividi invece sui token separati da spazi con >>: scegli il delimitatore usato dai tuoi dati.

Costruire stringhe in memoria

Nella direzione opposta, ostringstream assembla una stringa formattata senza il rischio di buffer overflow di sprintf né concatenazioni manuali fragili. È perfetto per righe di log, nomi di file o messaggi composti da numeri e testo:

Tutta la formattazione di <iomanip> che useresti con cout (setw, setfill, setprecision, hex) funziona allo stesso modo su uno string stream, quindi ottieni output con riempimento, a larghezza fissa o esadecimale catturato direttamente in una string.

La trappola: azzera prima di riusare

La trappola in cui cadono tutti è riusare uno stream senza azzerarlo. Quando uno stream arriva alla fine dei suoi dati, i flag eof e fail restano attivi, e ogni >> successivo non fa nulla in silenzio: nessun errore, solo valori vecchi:

stringstream ss("10");
int a, b;
ss >> a;            // a = 10, lo stream è alla fine -> flag eof attivo
ss.str("20");       // carica il nuovo testo...
ss >> b;            // FALLISCE IN SILENZIO: i flag di errore sono ancora attivi, b non cambia

Devi azzerare sia lo stato di errore sia il contenuto. L'ordine giusto è prima clear(), poi str():

Due errori correlati: ss.clear() azzera i flag ma non svuota il buffer (per quello usa ss.str("")), e uno stringstream a cui continui ad aggiungere con << continuerà a crescere: dentro un ciclo creane uno nuovo invece di riusarne uno sporco a ogni iterazione.

Prossimo passo: gli array

Gli string stream ti hanno dato un modo pulito per trasformare il testo in valori tipizzati e viceversa, spesso un'intera lista di valori, come il vector che hai riempito dividendo il CSV. Per memorizzare e indicizzare collezioni fisse di quei valori ti serve il container più fondamentale del linguaggio. Nella prossima pagina vedremo gli array: come dichiararli, accedervi tramite indice, ciclarci sopra ed evitare gli accessi fuori dai limiti che causano tanto undefined behavior in C++.

Domande frequenti

Cos'è uno stringstream in C++?

Uno std::stringstream è uno stream basato su una string invece che sulla tastiera o su un file. Ci scrivi con << e ci leggi con >>, proprio come con cout e cin, ed è quindi lo strumento standard per analizzare testo e costruire stringhe in memoria. Si trova nell'header <sstream>.

Come si converte una stringa in int in C++ con stringstream?

Metti il testo in uno stream ed estrai in un numero: stringstream ss("42"); int n; ss >> n;. Controlla if (ss) (o if (ss >> n)) per verificare che la conversione sia davvero riuscita. Per i casi semplici std::stoi è più breve, ma stringstream dà il meglio quando una stringa contiene più valori di tipo diverso.

Perché devo chiamare clear() su uno stringstream?

Quando uno stream arriva alla fine dei suoi dati, i bit eof/fail restano attivi e ogni >> successivo non fa nulla, senza avvisare. Se riusi lo stesso stringstream con un contenuto nuovo, chiama ss.clear() per azzerare quei flag di errore, poi ss.str(newText) per caricare i dati nuovi: altrimenti le letture falliscono in silenzio.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA