Sections

FG&A s.r.l.

Personal tools
You are here: Home Articoli Usare <sstream> invece di sprintf()
Document Actions

Usare <sstream> invece di sprintf()

Il C++ ha rivoluzionato il modo di usare l'I/O su stream e così per adeguarci al nuovo stile, dovremmo abbandonare le tanto familiari printf() e scanf() ed utilizzare invece gli operatori << e , abbinati a cin e cout.

Author: Mario Graziosi, mgnospam@fgasoftware.com

Il C++ ha rivoluzionato il modo di usare l'I/O su stream e così per adeguarci al nuovo stile, dovremmo abbandonare le tanto familiari printf() e scanf() ed utilizzare invece gli operatori << e , abbinati a cin e cout. Qualcuno potrebbe dire: 'ma tanto, con le GUI, chi ha più bisogno di stdin e stdout?'. Non è proprio così, anche se è vero che printf() ormai molto poco usata (almeno per ciò che riguarda Windows), non si può dire altrettanto di sprintf(), ossia la funzione che ci permette di formattare il risultato in una stringa anzichè su un file.

A volte, anche in un programma Windows, può essere necessario formattare un messaggio di errore, inserire una stringa formattata in una listbox, o semplicemente formattare (o interpretare) del testo prima di registrarlo in un DB. Per esempio, per formattare un intero, un double ed una string:

    char s[10] = "pippo";
double z = 10.12;
int i = 25;
usando il C convenzionale, dovremmo scrivere qualcosa come:
    #include <stdio.h>
char buf[30]
sprintf(buf, "s=%s, z=%lf, i=%d", s, z, i);
con il rischio di sforare l'area di memoria riservata ('buf' è limitato effettivamente a 29 caratteri) ed il rischio di avere un conflitto tra il tipo di dato e la stringa di formattazione (chi ci dice che, un domani, anche 'i' non diventi un intero e ci dimentichiamo di cambiare %d in %lf?)

Il mio consiglio è quello di adottare il nuovo stile di I/O stream del C++:

    #include <string>
#include <sstream>
using namespace std;
ostringstream ostr;
ostr << "s=" << s << ", z=" << z << ", i=" << i;
string buf = ostr.str();

Nell'esempio sopra siamo ora slegati dal tipo delle variabili e siamo anche sicuri che il buffer sarà abbastanza capiente (viene esteso automaticamente). Vediamo meglio il significato di ogni istruzione:

    ostringstream ostr;
nella riga sopra stiamo definendo un oggetto di tipo 'ostringstream' (per questo è necessario includere <sstream) che useremo successivamente per la formattazione. Questo oggetto è a tutti gli effetti uno stream e possiamo in qualsiasi momento estenderlo. L'istruzione
    ostr << "s=" << s << ", z=" << z << ", i=" << i;

è quella che effettivamente produce il risultato desiderato e, se avete un minimo di familiarità con gli stream del C++, non merita commenti. Infine, dall'oggetto 'ostr' dobbiamo ottenere la string:

    string buf = ostr.str();

Il metodo str() di ostringstream ci restituisce una stringa C++ standard (ossia, quella definita nel namespace 'std', da non confondere con CString (MFC) o con AnsiString (VCL)). Se noi volessimo ottenere direttamente una CString o una AnsiString, dovremmo scrivere qualcosa come:

    AnsiString buf = ostr.str().c_str(); // AnsiString di VCL

CString buf = ostr.str().c_str(); // CString di MFC

Le potenzialità di <sstream> sono supportare da C++ Builder 3 e lo dovrebbero essere anche da Visual C++ 5 (non ho eseguito i test, ma è abbastanza aderente agli standard). Sono invece scarsamente o poco supportate da BCB 1.x e VC++ 4.x e, se avete uno di questi compilatori, potete ripiegare su <stringstream> che sono classi meno standard, da tempo più diffuse e che permettono di ottenere risultati analoghi. L'esempio sopra dovrebbe essere riscritto così

    ostrstream ostr;
ostr << "s=" << s << ", z=" << z << ", i=" << i << ends;
const char* s = ost.str();

Quello che cambia è

  • 'ostr' non e' più 'ostringstream' ma un 'ostrstream',
  • dobbiamo aggiungere un 'tappo' alla fine e, per questo, usiamo 'ends',
  • il metodo str() questa volta non ci restituisce una string ma invece un puntatore ad un buffer che dovremo opportunamente copiare.

Se avessimo voluto formattare il risultato in un nostro buffer, avremmo potuto definire 'ostr' così

    char buf[256];
ostrstream ostr(buf, sizeof(buf));

ed il risultato sarebbe stato formattato in 'buf', assicurandoci di non sforare oltre i 256 byte (tappo compreso).