7

In C++ using std::to_string(), how should I pre-fill a string converted from integer? I tried using #include and std::setfill('0') but it didn't work. here's the simple test code.

#include <iostream>
#include <string>
//#include <iomanip> // setw, setfill below doesn't work

int main()
{
int i;
for (i=0;i<20;i++){
    std::cout << "without zero fill : " << std::to_string(i) << ", with zero fill : " << std::to_string(i) << std::endl;
    //std::cout << std::setw(3) << std::setfill('0') << "without zero fill : " << std::to_string(i) << ", with zero fill : " << std::to_string(i) << std::endl;  // doesn't work
}
}

What I want to do is, to have some numbers converted into string, but some of them with zero padding, the others not.(I'm using it to make filename actually.) How should I do it?
(I don't know why this should be not so simple as in C using %0d or %04d format specifier.)

ADD : From Add leading zero's to string, without (s)printf, I found

int number = 42;
int leading = 3; //6 at max
std::to_string(number*0.000001).substr(8-leading); //="042"

This works for me, but I'd prefer more natural solution, than this trick like method.

Chan Kim
  • 5,177
  • 12
  • 57
  • 112

2 Answers2

8

ostringstream seems to be an overkill. You can simply insert the number of zeros you need:

template<typename T/*, typename = std::enable_if_t<std::is_integral_v<T>>*/>
std::string to_string_with_zero_padding(const T& value, std::size_t total_length)
{
    auto str = std::to_string(value);
    if (str.length() < total_length)
        str.insert(str.front() == '-' ? 1 : 0, total_length - str.length(), '0');
    return str;
}

This function also works correctly if the value is negative and/or if T is char or related type.

Evg
  • 25,259
  • 5
  • 41
  • 83
  • Great idea. But some recommendations: 1) un-templatize it (avoids unnecessary recompilation in unchanged files), 2) add default to not fill, if not specified, and 3) parameterize the fill char used. `std::string toString (int val, std::size_t padLen = 0, char padChar = '0')` – automorphic Nov 23 '20 at 20:50
  • @automorphic I don't think it should be made a non-template function. We have many different integral types besides `int` (`std::to_string` has 9 overloads for different types). – Evg Nov 24 '20 at 08:41
  • Understood. For most projects, `int` would suffice. Untemplatizing it isn't really an improvement - just a simplification. But the other two improvements are the important ones, in that they extend the usefullness of your code without changing your logic. Thanks! – automorphic Nov 24 '20 at 21:43
3

Instead of using std::to_string(), you could use a std::ostringstream. The IO manipulators will work with a std::ostringstream.

#include <iostream>
#include <sstream>
#include <iomanip>

int main()
{
   for ( int i = 1; i <= 10; ++i )
   {
      std::ostringstream str;
      str << std::setw(3) << std::setfill('0') << i;
      std::cout << str.str() << std::endl;
   }
}

Output:

001
002
003
004
005
006
007
008
009
010

See it working at https://ideone.com/ay0Xzp.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • I see, thanks, but what if I want the final value as 'string' not 'ostringstream'? I want to use it as part of filename forming.. – Chan Kim Nov 26 '18 at 06:13
  • I see. that works! maybe I should understand this ostringstream. Why is the prints merged if I declarer it outside the for loop? – Chan Kim Nov 26 '18 at 06:19
  • 1
    @ChanKim, because in that case you are adding to the contents of the `ostringstream` object. By having it defined inside the loop, a new one is constructed in each iteration of the loop. – R Sahu Nov 26 '18 at 06:20