Click here to Skip to main content
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

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

 
Generalboost::format PinmemberRob Caldecott14-Dec-06 5:22 
GeneralLeft precision PinmemberThe Gilb14-Dec-06 5:07 
GeneralExcellent replacement for sprintf, Format Pinmembersupermyers21-Sep-06 6:11 
Questionstd::streams still slow and buggy? PinmemberWalterSchreppers30-Jun-06 5:55 
General#include Pinmemberdeterium10-Feb-06 6:57 
GeneralRe: #include PinmemberChristian Graus10-Feb-06 10:10 
GeneralFormat the precison to 3 digits. PinmemberSandeep. Vaidya30-Jan-06 2:38 
GeneralRe: Format the precison to 3 digits. PinmemberChristian Graus30-Jan-06 9:19 
Generalsnprintf is buffer overrun safe Pinmembernicophonic11-Jan-06 8:35 
GeneralRe: snprintf is buffer overrun safe Pinmemberdibeas1-Jun-06 0:14 
GeneralThanks alot for your tutorial Pinmemberionutcelgroaznic10-May-05 10:43 
Generalto round or not to round, that is the Q PinmemberWyattSouth31-Jan-05 6:30 
GeneralRe: to round or not to round, that is the Q Pinmemberkirkkorver11-Jul-05 12:19 
GeneralLegacy code Pinsussan_phu27-Oct-04 8:08 
GeneralRe: Legacy code PinmemberChristian Graus27-Oct-04 9:25 
Generala wee bit of silliness PinsussAnonymous21-Sep-04 17:47 
GeneralRe: a wee bit of silliness PinmemberChristian Graus22-Sep-04 10:19 
GeneralRe: a wee bit of silliness PinsussAnonymous28-Sep-04 19:47 
GeneralRe: a wee bit of silliness PinmemberChristian Graus29-Sep-04 10:08 
GeneralRe: a wee bit of silliness PinsussAnonymous6-Oct-04 21:14 
GeneralRe: a wee bit of silliness PinmemberChristian Graus7-Oct-04 9:54 
GeneralRe: a wee bit of silliness PinmemberAngus Comber20-Nov-07 1:18 
GeneralRe: a wee bit of silliness PinmemberWill Nolan17-May-08 4:01 
GeneralPatience and learning PinmemberKoushik Biswas25-Apr-06 8:55 
GeneralRe: a wee bit of silliness PinmemberDavid Piepgrass24-Jun-05 10:32 
GeneralRe: a wee bit of silliness [modified] Pinmemberxation30-May-06 21:22 
Generalostringstream vs ostrstream PinmemberPhil Harding9-Jul-04 13:23 
GeneralRe: ostringstream vs ostrstream PinmemberChristian Graus11-Jul-04 11:10 
GeneralRe: ostringstream vs ostrstream Pinmemberbeetung13-Aug-04 10:16 
GeneralPrecision Problem Nightmare! PinsussDavros66816-Feb-04 17:05 
GeneralRe: Precision Problem Nightmare! Pinsussan_phu27-Oct-04 8:02 
Generalostringstream limits PinsussPankaj Kohli19-Aug-03 8:09 
GeneralRe: ostringstream limits PinmemberChristian Graus19-Aug-03 11:05 
Generalprecision macro PinmemberRoger Allen8-Jan-03 7:24 
GeneralFloats and % PineditorAndrew Peace27-Mar-02 10:56 
GeneralRe: Floats and % PinmemberChristian Graus27-Mar-02 11:12 
GeneralRe: Floats and % PineditorAndrew Peace27-Mar-02 11:50 
Generalthank you christian.. PinmemberBernhard24-Mar-02 23:49 
GeneralRe: thank you christian.. PinmemberChristian Graus25-Mar-02 9:43 
GeneralWhy use evil macros PinmemberRobin21-Mar-02 16:26 
GeneralRe: Why use evil macros PinmemberNish [BusterBoy]21-Mar-02 16:45 
GeneralRe: Why use evil macros PinmemberChristian Graus21-Mar-02 17:49 
GeneralRe: Why use evil macros PinmemberRobin24-Mar-02 19:50 
GeneralRe: Why use evil macros PinmemberChristian Graus24-Mar-02 20:15 
GeneralRe: Why use evil macros PinmemberSimon Hughes24-Mar-02 22:45 
GeneralRe: Why use evil macros PinmemberChristian Graus24-Mar-02 23:01 
GeneralIs there a way ... PinmemberTim Smith21-Mar-02 13:57 
GeneralRe: Is there a way ... PinmemberChristian Graus21-Mar-02 14:34 
GeneralRe: Is there a way ... PinmemberVimal Earnest30-Nov-03 22:18 
GeneralRe: Is there a way ... PinmemberChristian Graus1-Dec-03 8:33 

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 | Mobile
Web03 | 2.8.140721.1 | Last Updated 21 Mar 2002
Article Copyright 2002 by Christian Graus
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid