Convert a number to a string with specified length in C++ - 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++?

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.

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

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

There are many ways of doing this. The simplest would be:
int n = 999;
char buffer[256]; sprintf(buffer, "%06d", n);
string str(buffer);

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

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

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.

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.

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

Related

Need a constant length in strings [duplicate]

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++?
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.
char str[7];
snprintf (str, 7, "%06d", n);
See snprintf
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)...
There are many ways of doing this. The simplest would be:
int n = 999;
char buffer[256]; sprintf(buffer, "%06d", n);
string str(buffer);
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;
}
stringstream will do (as xtofl pointed out). Boost format is a more convenient replacement for snprintf.
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.
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.
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;
}

How can I append an int to a tchar?

For testing purposes I would like each time I create a file name to add a number to it - the app I am testing has no user interface, and will likely create multiple files.
void somefunction()
{
static INT iFileNo = 0;
TCHAR tFileName[MAX_PATH] = L"c:/test/abcd.bmp";
iFileNo ++;
}
I would like to somehow append the iFileNo to the tFileName (to get something like abcd1.bmp, abcd2.bmp...)... How can I do that ?
Note: I am not using any string libraries
Minimal libraries... including
#include <STDLIB.H>
#include <STDIO.H>
#include <TCHAR.H>
Intended OS: Windows
You can make this hard, easy, or very easy. The middle of those would be something like:
void somefunction()
{
static unsigned int uiFileNo = 0;
static const WCHAR wSpec[] = L"c:/test/abcd%u.bmp";
WCHAR wFileName[MAX_PATH];
swprintf_s(wFileName, MAX_PATH, wSpec, uiFileNo++);
}
Uses swprintf_s, which would seem to fulfill your include-file restrictions (and assuming this is Windows, which your question seems to indicate).
Personally I's use std::wstring, but it appears you have reasons against it. Anyway, best of luck.
You've expressed a desire to not use any libraries. That's not a choice I would endorse, but since you asked...
The easiest way would be to decide on a fixed number of digits, with leading zeros. That makes the problem trivial. In this example I'm using two digits.
void somefunction()
{
static INT iFileNo = 0;
TCHAR tFileName[MAX_PATH] = L"c:/test/abcd00.bmp";
int iNumberOffset = 12;
if (iFileNo >= 100)
throw std::runtime_error("File number too large");
tFileName[iNumberOffset] += iFileNo / 10;
tFileName[iNumberOffset+1] += iFileNo % 10;
iFileNo ++;
}
You can use ostringstream:
std::ostringstream s;
s << "c:/test/abcd.bmp"
s << iFileNo++;
std::string filename = s.str();
If you want wide versions (as you're specifying L in your example) then use wstringstream and wstring.
If you really don't want to use a C++ library then you can use sprintf:
char filename[100];
sprint(filename, "c:/test/abcd.tmp-%d", iFileNo);
iFileNo++
You'll need to ensure that you buffer is big enough to hold the filename.
If you really don't want to use any libraries then Windows actually has it's own string formatting function, wsprintf which you can use instead of sprintf.

When to quantify ignored pattern match in the C sscanf function

Cppcheck 1.67 raised a portability issue in my source code at this line:
sscanf(s, "%d%*[,;.]%d", &f, &a);
This is the message I got from it:
scanf without field width limits can crash with huge input data on some versions of libc.
The original intention of the format string was to accept one of three possible limiter chars between two integers, and today - thanks to Cppcheck[1] - I see that %*[,;.] accepts even strings of limiter chars. However I doubt that my format string may cause a crash, because the unlimited part is ignored.
Is there possibly an issue with a buffer overrun? ...maybe behind the scenes?
[1]
How to get lost between farsightedness and blindness:
I tried to fix it by %1*[,;.] (after some API doc), but Cppcheck insisted in the issue, so I also tried %*1[,;.] with the same "success". Seems that I have to suppress it for now...
Congratulations on finding a bug in Cppcheck 1.67 (the current version).
You have basically three workarounds:
Just ignore the false positive.
Rework your format (assign that field, possible as you only want to match one character).
char tmp;
if(3 != sscanf(s, "%d %c%d", &f, &tmp, &a) || tmp!=',' && tmp!=';' && tmp!= '.')
goto error;
Suppress the warning directly (preferably inline-suppressions):
//cppcheck-suppress invalidscanf_libc
if(2 != sscanf(s, "%d%1*[,;.]%d", &f, &a))
goto error;
Don't forget to report the error, as "defect / false positive", so you can retire and forget that workaround as fast as possible.
When to quantify ignored pattern match in the C sscanf function?
Probably it's a good idea to always quantify (see below), but over-quantification may also distract from your intentions. In the above case, where a single separator char has to be skipped, the quantification would definitely be useful.
Is there possibly an issue with a buffer overrun? ...maybe behind the scenes?
There will be no crashes caused by your code. As to deal with the "behind the scenes" question, I experimented with large input strings. In the C library I tested, there was no internal buffer overflow. I tried the C lib that's shipped with Borland C++ 5.6.4 and found that I could not trigger a buffer overrun with large inputs (more than 400 million chars).
Surprisingly, Cppcheck was not totally wrong - there is a portability issue, but a different one:
#include <stdio.h>
#include <assert.h>
#include <sstream>
int traced_sscanf_set(const int count, const bool limited)
{
const char sep = '.';
printf("\n");
std::stringstream ss;
ss << "123" << std::string(count, sep) << "456";
std::string s = ss.str();
printf("string of size %d with %d '%c's in it\n", s.size(), count, sep);
std::stringstream fs;
fs << "%d%";
if (limited) {
fs << count;
}
fs << "*["<< sep << "]%d";
std::string fmt = fs.str();
printf("fmt: \"%s\"\n", fmt.c_str());
int a = 0;
int b = 0;
const sscanfResult = sscanf(s.c_str(), fmt.c_str(), &a, &b);
printf("sscanfResult=%d, a=%d, b=%d\n", sscanfResult, a, b);
return sscanfResult;
}
void test_sscanf()
{
assert(traced_sscanf_set(0x7fff, true)==2);
assert(traced_sscanf_set(0x7fff, false)==2);
assert(traced_sscanf_set(0x8000, true)==2);
assert(traced_sscanf_set(0x8000, false)==1);
}
The library I checked, internally limits the input consumed (and skipped) to 32767 (215-1) chars, if there is no explicitly specified limit in the format parameter.
For those who are interested, here is the trace output:
string of size 32773 with 32767 '.'s in it
fmt: "%d%32767*[.]%d"
sscanfResult=2, a=123, b=456
string of size 32773 with 32767 '.'s in it
fmt: "%d%*[.]%d"
sscanfResult=2, a=123, b=456
string of size 32774 with 32768 '.'s in it
fmt: "%d%32768*[.]%d"
sscanfResult=2, a=123, b=456
string of size 32774 with 32768 '.'s in it
fmt: "%d%*[.]%d"
sscanfResult=1, a=123, b=0

C++ get hour and minutes from string

I'm writing C++ code for school in which I can only use the std library, so no boost. I need to parse a string like "14:30" and parse it into:
unsigned char hour;
unsigned char min;
We get the string as a c++ string, so no direct pointer. I tried all variations on this code:
sscanf(hour.c_str(), "%hhd[:]%hhd", &hours, &mins);
but I keep getting wrong data. What am I doing wrong.
As everyone else has mentioned, you have to use %d format specified (or %u). As for the alternative approaches, I am not a big fan of the "because C++ has feature XX it must be used" and oftentimes resort to C-level functions. Though I never use scanf()-like stuff as it got its own problems. That being said, here is how I would parse your string using strtol() with error checking:
#include <cstdio>
#include <cstdlib>
int main()
{
unsigned char hour;
unsigned char min;
const char data[] = "12:30";
char *ep;
hour = (unsigned char)strtol(data, &ep, 10);
if (!ep || *ep != ':') {
fprintf(stderr, "cannot parse hour: '%s' - wrong format\n", data);
return EXIT_FAILURE;
}
min = (unsigned char)strtol(ep+1, &ep, 10);
if (!ep || *ep != '\0') {
fprintf(stderr, "cannot parse minutes: '%s' - wrong format\n", data);
return EXIT_FAILURE;
}
printf("Hours: %u, Minutes: %u\n", hour, min);
}
Hope it helps.
Your problem is, of course, that you are using sscanf. And that
you're using some very special type for the hours and minutes, instead
of int. Since you're parsing a string of exactly 5 characters, the
simplest solution is just to ensure that all of the characters are legal
in that position, using isdigit for characters 0, 1, 3 and 4, and
comparing to ':' for character 2. Once you've done that, it's trivial
to create an std::istringstream from the string, and input into an
int, a char (which you'll ignore afterwards) and a second int. If
you want to be more flexible in the input, for example allowing things
like "9:45" as well, you can skip the initial checks, and just input
into int, char and int, then check that the char contains ':'
(and that the two int are in range).
As to why your sscanf is failing: you're asking it to match something
like "12[:]34", which is not what you're giving it. I'm not sure
whether you're trying to use "%hhd:%hhd", or if for some reason you
really do want a character class, in which case, you have to use [ as
a conversion specifier, and then ignore the input: "%hhd%*[:]%hhd".
(This would allow accepting more than one character as the separator,
but otherwise, I don't see the advantage. Also, technically at least,
using %d and then passing the address of an unsigned integral types
is not supported, %hhd must be a signed char. In practice,
however, I don't think you'll ever run into any problems for
non-negative input values less than 128.)
As mentioned by izomorphius sscanf and variants are not C++ they are C. The C++ way would be to use streams. The following works (it's not amazingly flexible but should give you an idea)
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main(int argc, char* argv[])
{
string str = "14:30";
stringstream sstrm;
int hour,min;
sstrm << str;
sstrm >> hour;
sstrm.get(); // get colon
sstrm >> min;
cout << hour << endl;
cout << min << endl;
return 0;
}
You could also use getline to get everything upto the colon.
I would do it like this
unsigned tmp_hour, tmp_mins;
unsigned char hour, mins;
sscanf(hour.c_str(), "%u:%u", &tmp_hours, &tmp_mins);
hour = tmp_hours;
mins = tmp_mins;
Less messing around with obscure scanf options. I would add some error checking too.
My understanding is that h in %hhd is not a valid format specifier. The correct specifier for decimal integers is %d.
As R.Martinho Fernandes says in his comment, %d:%d will match two numbers separated by a colon (':').
Did you want something different?
You can always read the entire text string and parse it any way you want.
sscanf with %hhd:%hhd seems to work perfectly fine:
std::string time("14:30");
unsigned char hour, min;
sscanf(time.c_str(), "%hhd:%hhd", &hour, &min);
Note that the hh length modifier is simply to allow storing the value in an unsigned char.
However, sscanf is from the C Standard Library and there are better C++ ways to do this. A C++11 way to do this is using stoi:
std::string time("14:30");
unsigned char hour = std::stoi(time);
unsigned char min = std::stoi(time.substr(3));
In C++03, we can use stringstream instead but it's a bit of a pain if you really want it in a char:
std::stringstream stream("14:30");
unsigned int hour, min;
stream >> hour;
stream.ignore();
stream >> min;

C++ Convert char array to int representation

What is the best way to convert a char array (containing bytes from a file) into an decimal representation so that it can be converted back later?
E.g "test" -> 18951210 -> "test".
EDITED
It can't be done without a bignum class, since there's more letter combinations possible than integer combinations in an unsigned long long. (unsigned long long will hold about 7-8 characters)
If you have some sort of bignum class:
biguint string_to_biguint(const std::string& s) {
biguint result(0);
for(int i=0; i<s.length(); ++i) {
result *= UCHAR_MAX;
result += (unsigned char)s[i];
}
return result;
}
std::string biguint_to_string(const biguint u) {
std::string result;
do {
result.append(u % UCHAR_MAX)
u /= UCHAR_MAX;
} while (u>0);
return result;
}
Note: the string to uint conversion will lose leading NULLs, and the uint to string conversion will lose trailing NULLs.
I'm not sure what exactly you mean, but characters are stored in memory as their "representation", so you don't need to convert anything. If you still want to, you have to be more specific.
EDIT: You can
Try to read byte by byte shifting the result 8 bits left and oring it
with the next byte.
Try to use mpz_inp_raw
You can use a tree similar to Huffman compression algorithm, and then represent the path in the tree as numbers.
You'll have to keep the dictionary somewhere, but you can just create a constant dictionary that covers the whole ASCII table, since the compression is not the goal here.
There is no conversion needed. You can just use pointers.
Example:
char array[4 * NUMBER];
int *pointer;
Keep in mind that the "length" of pointer is NUMBER.
As mentioned, character strings are already ranges of bytes (and hence easily rendered as decimal numbers) to start with. Number your bytes from 000 to 255 and string them together and you've got a decimal number, for whatever that is worth. It would help if you explained exactly why you would want to be using decimal numbers, specifically, as hex would be easier.
If you care about compression of the underlying arrays forming these numbers for Unicode Strings, you might be interested in:
http://en.wikipedia.org/wiki/Standard_Compression_Scheme_for_Unicode
If you want some benefits of compression but still want fast random-access reads and writes within a "packed" number, you might find my "NSTATE" library to be interesting:
http://hostilefork.com/nstate/
For instance, if you just wanted a representation that only acommodated 26 english letters...you could store "test" in:
NstateArray<26> myString (4);
You could read and write the letters without going through a compression or decompression process, in a smaller range of numbers than a conventional string. Works with any radix.
Assuming you want to store the integers(I'm reading as ascii codes) in a string. This will add the leading zeros you will need to get it back into original string. character is a byte with a max value of 255 so it will need three digits in numeric form. It can be done without STL fairly easily too. But why not use tools you have?
#include <iostream>
#include <sstream>
using namespace std;
char array[] = "test";
int main()
{
stringstream out;
string s=array;
out.fill('0');
out.width(3);
for (int i = 0; i < s.size(); ++i)
{
out << (int)s[i];
}
cout << s << " -> " << out.str();
return 0;
}
output:
test -> 116101115116
Added:
change line to
out << (int)s[i] << ",";
output
test -> 116,101,115,116,