Solved

How to print into std::string like sprintf using C++11?

Posted on 2016-10-26
12
422 Views
Last Modified: 2016-10-28
In the past when I've needed a formatted string, I've created a char buffer and then did sprintf into that buffer.  Now that C++ has the std string class, I'd like to use it more, but it doesn't seem to play nice all the time.

I know I can still create the buffer, sprintf into that buffer and then convert to string, but is there a better way?  For example, if I have the following (junk code):

int i=1;
int j=4;
char buf[50];
sprintf(buf,"Pair (%d,%d) = %f\n",i,j,(double)i/j);

outputs:
Pair (1,4) = 0.25

How can I print that string directly into a std::string instead of going through a char buffer?


Using: C++11, Visual Studio 2013
0
Comment
Question by:ugeb
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 3
  • 2
  • +1
12 Comments
 
LVL 40

Expert Comment

by:evilrix
ID: 41861491
Use std: :stringstream.
0
 
LVL 25

Expert Comment

by:chaau
ID: 41861582
It is really strange why something like
CStrBuf is not implemented for std::string. I like this helper a lot
0
 
LVL 34

Assisted Solution

by:sarabande
sarabande earned 200 total points
ID: 41862062
How can I print that string directly into a std::string instead of going through a char buffer?

#include <string>
#include <sstream>
#include <iomanip>

int main()
{
    int i=1;
    int j=4;

    std::ostringstream oss1, oss2;
    oss1 << "Pair (" << i << "," << j << ") = " << (double(i)/j) << std::endl;
    std::string buf = oss1.str();

    //if you want to format the double you may add

    oss2 << "Pair (" << i << "," << j << ") = " << std::fixed << std::setprecision(1) << (double(i)/j) << std::endl;
    
    // the double result was rounded to one digit after the decimal point
    buf = oss2.str();

    return 0;
}

Open in new window


note, contrary to sprintf the above output statement was checked by the compiler. therefore it is less error-prone and safe against buffer-overflow.

Sara
0
[Live Webinar] The Cloud Skills Gap

As Cloud technologies come of age, business leaders grapple with the impact it has on their team's skills and the gap associated with the use of a cloud platform.

Join experts from 451 Research and Concerto Cloud Services on July 27th where we will examine fact and fiction.

 
LVL 11

Author Comment

by:ugeb
ID: 41862466
So these 2 lines:

char buf[50];
sprintf(buf,"Pair (%d,%d) = %f\n",i,j,(double)i/j);

Open in new window

which are simple and easy to read, turn into this convoluted mess?

std::ostringstream oss1, oss2;
oss2 << "Pair (" << i << "," << j << ") = " << std::fixed << std::setprecision(1) << (double(i)/j) << std::endl;
buf = oss2.str();

Open in new window

So I actually LOSE readability and conciseness in moving to std::string?  This is an enormous step sideways.  I have strings I need to generate with many more parameters and that would just make this code a mess.  Sounds like I just need to stick to the character buffer.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 41862467
>> note, contrary to sprintf the above output statement was checked by the compiler
Would have been nice if you'd mentioned you were building on my answer. http:#41861491

Unfortunately, I was in the car and so unable to give an example.

Nice example, BTW. :)

>> CStrBuf is not implemented for std::string. I like this helper a lot
Because you should use std::stringstream
0
 
LVL 40

Accepted Solution

by:
evilrix earned 300 total points
ID: 41862471
>> which are simple and easy to read, turn into this convoluted mess?
The thing is that the printf version is unsafe. As Sara points out, it's not checked by the compiler. The stringstream mechanism is not only safer, it is extensible. You can add support for your own types.

If you want to use the printf mechanism, but still keep it safe you could look at Boost Format.

http://www.boost.org/doc/libs/1_62_0/libs/format/
0
 
LVL 40

Expert Comment

by:evilrix
ID: 41862479
It's also quite readable when formatted properly:

auto && oss = std::ostringstream();

oss
   << "Pair (" << i << "," << j << ") = "
   << std::fixed << std::setprecision(1)
   << (double(i)/j)
   << std::endl;

auto && buf = oss.str();

Open in new window

0
 
LVL 40

Expert Comment

by:evilrix
ID: 41862491
BTW: If you really wanted to use sprintf...

auto && buf = std::string();
buf.resize(N); // where N is the size of the string buffer you need;
sprintf(&buf[0], "Pair (%d,%d) = %f\n", i, j, (double)i/j);

std::cout << buf << std::endl;

Open in new window


I don't recommend this, but it's a way of doing what you want and still using std::string.
0
 
LVL 11

Author Closing Comment

by:ugeb
ID: 41862513
@evilrix,  with your reformatting it is a little better to read, but I would have to spend much more time interpreting how it would look on execution.  And, it takes 3x as long to write it, you have all the "<<" getting in the way.

While the Boost lib is a good idea, it should not be necessary.  C++ should, by this point, have far better string functionality built in, especially formatting.  While I had occasional issues with printf and sprintf, they are far superior, in my opinion, to streams in terms of readability and development time.

I wish I could stick with Python ...
0
 
LVL 11

Author Comment

by:ugeb
ID: 41862522
@evilrix,

I like that new solution. It seems like a bit of a kluge, but in this case it might just work well.  It seems it would have the same issues as my original code, except I can avoid the char buffer now, correct?
0
 
LVL 40

Expert Comment

by:evilrix
ID: 41862663
>> C++ should, by this point, have far better string functionality built in
It does, stringstream. It's like anything, it takes a little time to get used to it; however, it is better. Like I said, not only is it compile time safe (unlike sprintf), it is designed to be extensible so that you can stream your own objects. Yes, it's more verbose, but verbose doesn't mean it's not better. Trust me when I say that using sprintf really isn't the way to go. Whilst it may seem better (because it's what you are used to), it's really not. It's a dangerous function that is the cause of many hard to uncover code defects. Specifically, it can lead to exploits in code that you just can get with stringstream.

I do agree that stringstream does take some getting used to, but you can do something like this that you couldn't do with sprintf...

struct mypair
{
   int x;
   int y;
};

ostream operator << (ostream & out, mypair const & pair)
{
   oss
      << "Pair (" <<pair.x << "," << pair.y << ") = "
      << std::fixed << std::setprecision(1)
      << (double(pair.x)/pair.y);
}

auto && oss = std::ostringstream();

auto && pair = mypair{1, 2};

oss << pair << std::endl;

pair.x = 2;
pair.y = 4;

oss << pair << std::endl;

Open in new window


auto && s = oss.str();

std::out << s << std::endl;
0
 
LVL 34

Expert Comment

by:sarabande
ID: 41863639
Unfortunately, I was in the car and so unable to give an example.
sorry, actually i waited some time for you to give  a sample yourself.


[quoteAnd, it takes 3x as long to write it, you have all the "<<" getting in the way.
[/quote]

you were comparing

   
sprintf(buf,"Pair (%d,%d) = %f\n",i,j,(double)i/j);

Open in new window


with code

 
oss1 << "Pair (" << i << "," << j << ") = " << (double(i)/j) << "\n";

Open in new window


and said the second is a mess and less readable. your code has 50 characters, the other code has 54  (spaces outside of literals not counted). the first is good readable for the author if you only look for the formatting and not for the final output. after a few months you will need some time to find out which format specifier belongs to which argument. the second can be read from left to right and with a few efforts you could have (as shown by evilrix)

oss1 << Pair(i, j) << "\n";

Open in new window


if you add the following to a header file

#define STREAM(s) (((std::ostringstream&)(std::ostringstream() << s)).str())

Open in new window


you can do like

std::string s = STREAM(i << "/" << j << " = " << (1.*i)/j); 

Open in new window


or

if (ret != 0)
{
     std::cerr << STREAM("Error " << ret << ", in SomeFunction(" << arg1 << ", " << arg2 << ")\n");
}

Open in new window


do you still think it is a mess?

Sara
0

Featured Post

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

627 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question