59

I have some numbers of different length (like 1, 999, 76492, so on) and I want to convert them all to strings with a common length (for example, if the length is 6, then those strings will be: '000001', '000999', '076492').

In other words, I need to add correct amount of leading zeros to the number.

int n = 999;
string str = some_function(n,6);
//str = '000999'

Is there a function like this in C++?

Brian R. Bondy
  • 339,232
  • 124
  • 596
  • 636
Degvik
  • 3,050
  • 6
  • 25
  • 20

9 Answers9

82

or using the stringstreams:

#include <sstream>
#include <iomanip>

std::stringstream ss;
ss << std::setw(10) << std::setfill('0') << i;
std::string s = ss.str();

I compiled the information I found on arachnoid.com because I like the type-safe way of iostreams more. Besides, you can equally use this code on any other output stream.

Ivan Mushketyk
  • 8,107
  • 7
  • 50
  • 67
xtofl
  • 40,723
  • 12
  • 105
  • 192
18
char str[7];
snprintf (str, 7, "%06d", n);

See snprintf

Mateus
  • 28
  • 6
Isak Savo
  • 34,957
  • 11
  • 60
  • 92
  • While it's good practice to always use snprintf() this is one of the few places where you can safely use sprintf(). – Mark Baker Oct 22 '08 at 12:27
  • Yeah I know. But I tend to always use snprintf because there's really no reason not to (performance diff is negligible). – Isak Savo Oct 22 '08 at 14:04
  • 6
    arg #2 is a size_t, not a len, so it's 7, not 6. Better use sizeof however. – PW. Oct 22 '08 at 14:43
  • Not sure if I'm missing something, but I think it also needs the type in the format string, so "%06d" or similar – JSoet Nov 18 '15 at 00:05
  • 1
    First argument of snprintf should be `str`, not `buf` – Duncan May 04 '17 at 07:08
12

One thing that you may want to be aware of is the potential locking that may go on when you use the stringstream approach. In the STL that ships with Visual Studio 2008, at least, there are many locks taken out and released as various locale information is used during formatting. This may, or may not, be an issue for you depending on how many threads you have that might be concurrently converting numbers to strings...

The sprintf version doesn't take any locks (at least according to the lock monitoring tool that I'm developing at the moment...) and so might be 'better' for use in concurrent situations.

I only noticed this because my tool recently spat out the 'locale' locks as being amongst the most contended for locks in my server system; it came as a bit of a surprise and may cause me to revise the approach that I've been taking (i.e. move back towards sprintf from stringstream)...

Len Holgate
  • 21,282
  • 4
  • 45
  • 92
  • It does make sense that the locale is used, but indeed, that it is locked... Valuable info that is! – xtofl Oct 22 '08 at 19:31
  • It may only be the Visual Studio STL's that do this, I haven't checked with a test program built with STLPort. I also haven't investigated to see why it gets locked. – Len Holgate Oct 22 '08 at 21:19
  • STLPort 5.1.5 doesn't exhibit this cross thread contention problem, but the sprintf style of conversion is still around 3 times faster... – Len Holgate Dec 19 '08 at 22:26
  • Here it is the interesting post by Len Holgate: http://www.lenholgate.com/archives/000824.html – Alessandro Jacopson Dec 21 '08 at 09:38
4

There are many ways of doing this. The simplest would be:

int n = 999;
char buffer[256]; sprintf(buffer, "%06d", n);
string str(buffer);
Pramod
  • 9,256
  • 4
  • 26
  • 27
  • in this case you might actually want to use: sprintf (buffer, "%06d", n); note the 0 in front of the 6 that you need to padd with zeroes – Nathan Fellman Oct 22 '08 at 11:35
  • A buffer of size 256 is _way_ overkill for this purpose. Even though the number can overflow 7 chars (which Isak's answer deals with, by using snprintf), still, no int I know of takes up 256 chars. :-P – C. K. Young Oct 22 '08 at 11:45
  • Good point Chris. I think the largest int is actually just over 999,999,999,999,999, so char buffer[17] is perfectly safe, allowing for the \0 and possibly a sign (though I don't think the largest negative int would be anywhere near that long). – Matt Curtis Oct 22 '08 at 11:52
  • The largest int is implementation-dependent. Most modern compilers use a 32-bit two's complement int, meaning that the largest int is 2,147,483,647 and the longest int is the min int, -2,147,483,648. This is 12 chars long counting the terminating NUL. For portable code, an int can be ANY length. – Steve Jessop Oct 22 '08 at 12:01
  • First this isn't c++, and the largest int is 2147483648 which is only 10 characters long. Where you got the 999,999,999,999,999 from I have no idea. An unsigned it is larger, but still has the same number of characters. – graham.reeds Oct 22 '08 at 12:02
  • @graham: for a signed integer you also have to take a sign into account. The longest number is -2147483648 is 11 characters long (not counting a null-byte) – Leon Timmermans Oct 22 '08 at 12:04
  • Despite the supposed ease, sprintf is a bad solution. snprintf or stringstream are preferable. – Kristopher Johnson Oct 22 '08 at 12:08
  • Stack space is free to allocate, and if you're going to overflow stack just because you allocated a 256 byte sized buffer, you've got bigger problems. While snprintf is indeed safer, it is only in C99. Stringstream is going to have to do some memory allocation under hood, so is slightly slower. – Pramod Oct 22 '08 at 12:48
  • I think you've been downvoted by C++ purists with lots of "lab" experience... you were doing alright until someone mentioned omfgstringstreams. – Matt Curtis Oct 22 '08 at 13:06
  • @Pramod: he wants a string at the end anyway, so memory allocation is needed there. I suspect (but can't be bothered to make sure) STL implementations will do no more allocation in xtofl's solution than in yours. Yours just saves it for the last line. – Steve Jessop Oct 22 '08 at 14:45
4

This method doesn't use streams nor sprintf. Other than having locking problems, streams incur a performance overhead and is really an overkill. For streams the overhead comes from the need to construct the steam and stream buffer. For sprintf, the overhead comes from needing to interpret the format string. This works even when n is negative or when the string representation of n is longer than len. This is the FASTEST solution.

inline string some_function(int n, int len)
{
    string result(len--, '0');
    for (int val=(n<0)?-n:n; len>=0&&val!=0; --len,val/=10)
       result[len]='0'+val%10;
    if (len>=0&&n<0) result[0]='-';
    return result;
}
sep
  • 3,409
  • 4
  • 29
  • 32
3

stringstream will do (as xtofl pointed out). Boost format is a more convenient replacement for snprintf.

Community
  • 1
  • 1
PW.
  • 3,727
  • 32
  • 35
3

This is an old thread, but as fmt might make it into the standard, here is an additional solution:

#include <fmt/format.h>

int n = 999;

const auto str = fmt::format("{:0>{}}", n, 6);

Note that the fmt::format("{:0>6}", n) works equally well when the desired width is known at compile time. Another option is abseil:

#include <absl/strings/str_format.h>

int n = 999;

const auto str = absl::StrFormat("%0*d", 6, n);

Again, abs::StrFormat("%06d", n) is possible. boost format is another tool for this problem:

#include <boost/format.hpp>

int n = 999;

const auto str = boost::str(boost::format("%06d") % n);

Unfortunately, variable width specifier as arguments chained with the % operator are unsupported, this requires a format string setup (e.g. const std::string fmt = "%0" + std::to_string(6) + "d";).

In terms of performance, abseil and fmt claim to be very attractive and faster than boost. In any case, all three solutions should be more efficient than std::stringstream approaches, and other than the std::*printf family, they do not sacrifice type safety.

lubgr
  • 37,368
  • 3
  • 66
  • 117
1

sprintf is the C-like way of doing this, which also works in C++.

In C++, a combination of a stringstream and stream output formatting (see http://www.arachnoid.com/cpptutor/student3.html ) will do the job.

Chris Johnson
  • 10,469
  • 4
  • 31
  • 35
0

From C++ 11, you can do:

 string to_string(unsigned int number, int length) {
    
    string num_str = std::to_string(number);
    
    if(num_str.length() >= length) return num_str;
    
    string leading_zeros(length - num_str.length(), '0');
    
    return leading_zeros + num_str;
}

If you also need to handle negative numbers, you can rewrite the function as below:

string to_string(int number, int length) {
    
    string num_str = std::to_string(number);

    if(num_str.length() >= length) return num_str;

    string leading_zeros(length - num_str.length(), '0');
    
    //for negative numbers swap the leading zero with the leading negative sign
    if(num_str[0] == '-') {  
        num_str[0] = '0';
        leading_zeros[0] = '-';
    }

    return leading_zeros + num_str;
}
mhc
  • 289
  • 2
  • 8