Degvik
Degvik

Reputation: 3180

Convert a number to a string with specified length in C++

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++?

Upvotes: 67

Views: 94710

Answers (10)

vitaut
vitaut

Reputation: 55524

You can do it with C++20 std::format (godbolt):

int n = 999;
std::string str = std::format("{:0{}}", n, 6);
// str == "000999"

which is similar to sprintf but type- and memory-safe and uses Python-like format string syntax.

On older systems you can use the {fmt} library, std::format is based on.

Disclaimer: I'm the author of {fmt} and C++20 std::format.

Upvotes: 3

mhc
mhc

Reputation: 299

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;
}

Upvotes: 0

lubgr
lubgr

Reputation: 38267

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.

Upvotes: 4

Isak Savo
Isak Savo

Reputation: 35884

char str[7];
snprintf (str, 7, "%06d", n);

See snprintf

Upvotes: 21

xtofl
xtofl

Reputation: 41509

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.

Upvotes: 90

Len Holgate
Len Holgate

Reputation: 21616

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)...

Upvotes: 14

sep
sep

Reputation: 3485

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;
}

Upvotes: 4

PW.
PW.

Reputation: 3725

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

Upvotes: 2

Chris Johnson
Chris Johnson

Reputation: 10680

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.

Upvotes: 1

Pramod
Pramod

Reputation: 9456

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

int n = 999;
char buffer[256]; sprintf(buffer, "%06d", n);
string str(buffer);

Upvotes: 4

Related Questions