Click here to Skip to main content
11,429,154 members (62,911 online)
Click here to Skip to main content

OStringStream, or how to stop worrying and never use sprintf again

, 20 Mar 2002
Rate this:
Please Sign up or sign in to vote.
A typesafe alternative to sprintf from the std library

Introduction

There are two main reasons why people use CString over std:string.  The first is that a CString converts easily between a BSTR and a char *.  The way to overcome this is to #include<comdef>, and cast your std::string.c_str() to a _bstr_t.  The second problem requires a little more explanation...

CString has a very handy method called Format.  This method works the same way as sprintf, which is hardly surprising as internally it takes an educated guess as to the new buffer size and then calls _vstprintf.  The problem with using sprintf yourself or relying on functions that do so is twofold.  First of all, you need to make memory allocations beyond what you need in order to minimise the possibility of a buffer over-run, a possibility you can do nothing to get rid of entirely.  Secondly, because you need to provide variables, and formatting information separately, there is no type safety, and a real cause for error.

None of this is completely life threatening, and plenty of commercial apps I am sure use sprintf with no problem.  However, it just isn't best practice.  The highly under-rated iostream library provides a much better option, in the form of ostringstream.  This class provides a buffered stream, and returns us a std::string containing the information we have streamed to it.  Because it is an iostream, we can pass any type into it so long as a stream handler exists for it.  I'm not going to cover writing your own in this article, but it is really is very easy to do, and then you can stream any types you may create.

So the basic idea is that we create an ostringstream and then pass information into it, like this:

ostringstream strstrm;
strstrm << "This is how I pass in text, but I can pass in numbers as easily - " 
        << 42 << endl;

We can then access the underlying string using the str() method, which we can chain to the strings c_str() method to get a const char * if desired.  The endl is an example of a stream modifier - it added a newline character and flushes the stream.

cout << strstrm.str();

The str method can also be passed a new value for the buffer, so we can clear it like this:

strstrm.str("");

Now we've got the basics under our belt, lets see what we can do to implement sprintf type behaviour.  Obviously we can pass in numbers, but what if we want to format them ? Well, as the following code shows, we can do a lot more than sprintf does.  We can easily switch between hex, oct and decimal, we can get bools to be printed as words ( a note, BOOL is NOT a bool, it is a typedef for an int.  iostreams will always regard BOOL to be an int, this is your fault for using BOOL, which Microsoft created for C programming, as C does not have a bool type ), we can format floating point numbers as scientific notation, etc.  Note that when we use fixed point we get 6 decimal places, if we just stream a float, we get six significant figures overall.  That is, if you add another significant figure to the left of the decimal point in the example, you'll lose a figure to the right.

Here is the code for the example, followed by it's output.

string str;
ostringstream strstrm;
strstrm << boolalpha << true << " is a bool value" << noboolalpha 
        << ", so is " << false; cout << strstrm.str() << endl;
strstrm.str("");

strstrm << "100 decimal = " << dec << 100 << ", octal = " << 100 
        << ", hex = " << hex << 100 << endl;
cout << strstrm.str();
strstrm.str("");

float f = 199.9273f;
strstrm << "A float: " << f << ", scientific notation " << f 
        << " " << scientific << f << " fixed notation " << fixed 
        << f << endl;
cout << strstrm.str();
strstrm.str("");

strstrm << "Formatted to two decimal places " << setprecision(2) 
<< f << ", formatted to %2.5f " << setprecision(5) << 
leftprec(f, 2) << endl; cout << strstrm.str();
 
true is a bool value, so is 0
100 decimal = 100, octal = 144, hex = 64
A float: 199.927, scientific notation 199.927 1.999273e+002 fixed notation 199.927307
Formatted to two decimal places 199.93, formatted to %2.5f 99.92731

Now, there is one little trick in here.  The modifiers boolalpha/noboolalpha turn alpha booleans off and on.    dec/octal/hex changes the number base (setbase(8/10/16) does the same), scientific/fixed change the way floats are shown (uppercase/nouppercase allows you to set the case of the E in scientific notation, and showpos/noshowpos allows you to force a + sign for positive numbers ), and setprecision allows you to set the precision to the right of the decimal point.  However, no method is provided to set the precision to the left of the decimal point. 

My first thought in providing such a method was to use the % operator, however it does not work with floats ( why? ).  So in order to provide such a method I had to write the following macro.

#define leftprec(x,y) (x - (((int)floor(x) - (((int)floor(x))% ((int)(pow(10,y)))))))

And people ask me why I think macros are ugly....  Anyhow, you can use setprecision to set precision to the right of the decimal, then pass in the value and the number of significant characters you want to the left of the decimal.  This provides no rounding, it will just strip the extra values.  Note also you'll need to #include<math.h>

I'm afraid I built this project using VS.NET beta 2, so the provided project will not work with VC 6.  However, it is a simple task to create an empty console app and copy the main() function from the provided cpp file into it, so I don't feel it's too much of a problem.  I hope this article has made you realise some of the power provided by the iostream library, as well as dispelling any reason you may have for feeling that life cannot go on without CString.  The standard string does everything CString does, it's just that sometimes it's not as clear, because std::string provides some of this functionality through iostreams, and some through the fact it is an STL container, which means that all the algorithms provided by the STL work with it.  In effect, std::string is FAR more powerful than CString, if you know where to look.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Christian Graus
Software Developer (Senior)
Australia Australia
Programming computers ( self taught ) since about 1984 when I bought my first Apple ][. Was working on a GUI library to interface Win32 to Python, and writing graphics filters in my spare time, and then building n-tiered apps using asp, atl and asp.net in my job at Dytech. After 4 years there, I've started working from home, at first for Code Project and now for a vet telemedicine company. I owned part of a company that sells client education software in the vet market, but we sold that and I worked for the owners for five years before leaving to get away from the travel, and spend more time with my family. I now work for a company here in Hobart, doing all sorts of Microsoft based stuff in C++ and C#, with a lot of T-SQL in the mix.

Comments and Discussions

 
QuestionMay be buffer safe, but no replacement for sprintf() Pin
Member 114961673-Mar-15 15:25
memberMember 114961673-Mar-15 15:25 
AnswerRe: May be buffer safe, but no replacement for sprintf() Pin
Christian Graus3-Mar-15 16:07
protectorChristian Graus3-Mar-15 16:07 
Generalboost::format Pin
Rob Caldecott14-Dec-06 6:22
memberRob Caldecott14-Dec-06 6:22 
GeneralLeft precision Pin
The Gilb14-Dec-06 6:07
memberThe Gilb14-Dec-06 6:07 
GeneralExcellent replacement for sprintf, Format Pin
supermyers21-Sep-06 7:11
membersupermyers21-Sep-06 7:11 
Questionstd::streams still slow and buggy? Pin
WalterSchreppers30-Jun-06 6:55
memberWalterSchreppers30-Jun-06 6:55 
General#include Pin
deterium10-Feb-06 7:57
memberdeterium10-Feb-06 7:57 
GeneralRe: #include Pin
Christian Graus10-Feb-06 11:10
memberChristian Graus10-Feb-06 11:10 
GeneralFormat the precison to 3 digits. Pin
Sandeep. Vaidya30-Jan-06 3:38
memberSandeep. Vaidya30-Jan-06 3:38 
GeneralRe: Format the precison to 3 digits. Pin
Christian Graus30-Jan-06 10:19
memberChristian Graus30-Jan-06 10:19 
Generalsnprintf is buffer overrun safe Pin
nicophonic11-Jan-06 9:35
membernicophonic11-Jan-06 9:35 
GeneralRe: snprintf is buffer overrun safe Pin
dibeas1-Jun-06 1:14
memberdibeas1-Jun-06 1:14 
GeneralThanks alot for your tutorial Pin
ionutcelgroaznic10-May-05 11:43
memberionutcelgroaznic10-May-05 11:43 
Generalto round or not to round, that is the Q Pin
WyattSouth31-Jan-05 7:30
memberWyattSouth31-Jan-05 7:30 
GeneralRe: to round or not to round, that is the Q Pin
kirkkorver11-Jul-05 13:19
memberkirkkorver11-Jul-05 13:19 
GeneralLegacy code Pin
an_phu27-Oct-04 9:08
sussan_phu27-Oct-04 9:08 
GeneralRe: Legacy code Pin
Christian Graus27-Oct-04 10:25
memberChristian Graus27-Oct-04 10:25 
Generala wee bit of silliness Pin
Anonymous21-Sep-04 18:47
sussAnonymous21-Sep-04 18:47 
GeneralRe: a wee bit of silliness Pin
Christian Graus22-Sep-04 11:19
memberChristian Graus22-Sep-04 11:19 
GeneralRe: a wee bit of silliness Pin
Anonymous28-Sep-04 20:47
sussAnonymous28-Sep-04 20:47 
GeneralRe: a wee bit of silliness Pin
Christian Graus29-Sep-04 11:08
memberChristian Graus29-Sep-04 11:08 
GeneralRe: a wee bit of silliness Pin
Anonymous6-Oct-04 22:14
sussAnonymous6-Oct-04 22:14 
GeneralRe: a wee bit of silliness Pin
Christian Graus7-Oct-04 10:54
memberChristian Graus7-Oct-04 10:54 
GeneralRe: a wee bit of silliness Pin
Angus Comber20-Nov-07 2:18
memberAngus Comber20-Nov-07 2:18 
GeneralRe: a wee bit of silliness Pin
Will Nolan17-May-08 5:01
memberWill Nolan17-May-08 5:01 
GeneralPatience and learning Pin
Koushik Biswas25-Apr-06 9:55
memberKoushik Biswas25-Apr-06 9:55 
GeneralRe: a wee bit of silliness Pin
David Piepgrass24-Jun-05 11:32
memberDavid Piepgrass24-Jun-05 11:32 
GeneralRe: a wee bit of silliness [modified] Pin
xation30-May-06 22:22
memberxation30-May-06 22:22 
Generalostringstream vs ostrstream Pin
Phil Harding9-Jul-04 14:23
memberPhil Harding9-Jul-04 14:23 
GeneralRe: ostringstream vs ostrstream Pin
Christian Graus11-Jul-04 12:10
memberChristian Graus11-Jul-04 12:10 
GeneralRe: ostringstream vs ostrstream Pin
beetung13-Aug-04 11:16
memberbeetung13-Aug-04 11:16 
GeneralPrecision Problem Nightmare! Pin
Davros66816-Feb-04 18:05
sussDavros66816-Feb-04 18:05 
GeneralRe: Precision Problem Nightmare! Pin
an_phu27-Oct-04 9:02
sussan_phu27-Oct-04 9:02 
Generalostringstream limits Pin
Pankaj Kohli19-Aug-03 9:09
sussPankaj Kohli19-Aug-03 9:09 
GeneralRe: ostringstream limits Pin
Christian Graus19-Aug-03 12:05
memberChristian Graus19-Aug-03 12:05 
Generalprecision macro Pin
Roger Allen8-Jan-03 8:24
memberRoger Allen8-Jan-03 8:24 
GeneralFloats and % Pin
Andrew Peace27-Mar-02 11:56
editorAndrew Peace27-Mar-02 11:56 
GeneralRe: Floats and % Pin
Christian Graus27-Mar-02 12:12
memberChristian Graus27-Mar-02 12:12 
GeneralRe: Floats and % Pin
Andrew Peace27-Mar-02 12:50
editorAndrew Peace27-Mar-02 12:50 
Generalthank you christian.. Pin
Bernhard25-Mar-02 0:49
memberBernhard25-Mar-02 0:49 
GeneralRe: thank you christian.. Pin
Christian Graus25-Mar-02 10:43
memberChristian Graus25-Mar-02 10:43 
GeneralWhy use evil macros Pin
Robin21-Mar-02 17:26
memberRobin21-Mar-02 17:26 
GeneralRe: Why use evil macros Pin
Nish [BusterBoy]21-Mar-02 17:45
memberNish [BusterBoy]21-Mar-02 17:45 
GeneralRe: Why use evil macros Pin
Christian Graus21-Mar-02 18:49
memberChristian Graus21-Mar-02 18:49 
GeneralRe: Why use evil macros Pin
Robin24-Mar-02 20:50
memberRobin24-Mar-02 20:50 
GeneralRe: Why use evil macros Pin
Christian Graus24-Mar-02 21:15
memberChristian Graus24-Mar-02 21:15 
GeneralRe: Why use evil macros Pin
Simon Hughes24-Mar-02 23:45
memberSimon Hughes24-Mar-02 23:45 
GeneralRe: Why use evil macros Pin
Christian Graus25-Mar-02 0:01
memberChristian Graus25-Mar-02 0:01 
GeneralIs there a way ... Pin
Tim Smith21-Mar-02 14:57
memberTim Smith21-Mar-02 14:57 
GeneralRe: Is there a way ... Pin
Christian Graus21-Mar-02 15:34
memberChristian Graus21-Mar-02 15:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150428.2 | Last Updated 21 Mar 2002
Article Copyright 2002 by Christian Graus
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid