问题
I am trying to do this simple task. Just to format a number using C or C++, but under Windows CE programming.
In this environment, neither inbue nor setlocale methods work.
Finally I did this with no success:
char szValue[10];
sprintf(szValue, "%'8d", iValue);
Any idea?
回答1:
Here's one way - create a custom locale and imbue it with the appropriately customised facet:
#include <locale>
#include <iostream>
#include <memory>
struct separate_thousands : std::numpunct<char> {
char_type do_thousands_sep() const override { return ','; } // separate with commas
string_type do_grouping() const override { return "\3"; } // groups of 3 digit
};
int main()
{
int number = 123'456'789;
std::cout << "default locale: " << number << '\n';
auto thousands = std::make_unique<separate_thousands>();
std::cout.imbue(std::locale(std::cout.getloc(), thousands.release()));
std::cout << "locale with modified thousands: " << number << '\n';
}
expected output:
default locale: 123456789
locale with modified thousands: 123,456,789
回答2:
Why re-invent the wheel and not use functions that are provided for this? See GetNumberFormat.
Custom formatting can be done using the correct NUMBERFMT structure values. For example (pseudo-code):
//Display three digits after the decimal point.
.NumDigits = 3
//Display zeros after the decimal point.
.LeadingZero = 1
//Group every three digits to the left of the decimal.
.Grouping = 3
//Use a comma to as the decimal point (like they do in France and Spain).
.lpDecimalSep = ","
//Likewise, use a period as the grouping separator.
.lpThousandSep = "."
//Put the negative sign immediately after the number.
.NegativeOrder = 3
回答3:
These functions work in C++, for numbers in string, with or without decimals.
Next function not support negative string numbers or decimal separators, but it was very simple:
std::string quickAddThousandSeparators(std::string value, char thousandSep = ',')
{
int len = value.length();
int dlen = 3;
while (len > dlen) {
value.insert(len - dlen, 1, thousandSep);
dlen += 4;
len += 1;
}
return value;
}
Next function support negative string numbers and decimal separators:
std::string addThousandSeparators(std::string value, char thousandSep = ',', char decimalSep = '.', char sourceDecimalSep = '.')
{
int len = value.length();
int negative = ((len && value[0] == '-') ? 1: 0);
int dpos = value.find_last_of(sourceDecimalSep);
int dlen = 3 + (dpos == std::string::npos ? 0 : (len - dpos));
if (dpos != std::string::npos && decimalSep != sourceDecimalSep) {
value[dpos] = decimalSep;
}
while ((len - negative) > dlen) {
value.insert(len - dlen, 1, thousandSep);
dlen += 4;
len += 1;
}
return value;
}
And gtest passed tests:
TEST (quickAddThousandSeparators, basicNumbers) {
EXPECT_EQ("", quickAddThousandSeparators(""));
EXPECT_EQ("1", quickAddThousandSeparators("1"));
EXPECT_EQ("100", quickAddThousandSeparators("100"));
EXPECT_EQ("1,000", quickAddThousandSeparators("1000"));
EXPECT_EQ("10,000", quickAddThousandSeparators("10000"));
EXPECT_EQ("100,000", quickAddThousandSeparators("100000"));
EXPECT_EQ("1,000,000", quickAddThousandSeparators("1000000"));
EXPECT_EQ("1,000,000,000", quickAddThousandSeparators("1000000000"));
EXPECT_EQ("1,012,789,345,456,123,678,456,345,809", quickAddThousandSeparators("1012789345456123678456345809"));
}
TEST (quickAddThousandSeparators, changeThousandSeparator) {
EXPECT_EQ("", quickAddThousandSeparators("",'.'));
EXPECT_EQ("1", quickAddThousandSeparators("1",'.'));
EXPECT_EQ("100", quickAddThousandSeparators("100", '.'));
EXPECT_EQ("1.000", quickAddThousandSeparators("1000", '.'));
EXPECT_EQ("1.000.000.000", quickAddThousandSeparators("1000000000", '.'));
EXPECT_EQ("1.012.789.345.456.123.678.456.345.809", quickAddThousandSeparators("1012789345456123678456345809", '.'));
}
TEST (addThousandSeparators, basicNumbers) {
EXPECT_EQ("", addThousandSeparators(""));
EXPECT_EQ("1", addThousandSeparators("1"));
EXPECT_EQ("100", addThousandSeparators("100"));
EXPECT_EQ("1,000", addThousandSeparators("1000"));
EXPECT_EQ("10,000", addThousandSeparators("10000"));
EXPECT_EQ("100,000", addThousandSeparators("100000"));
EXPECT_EQ("1,000,000", addThousandSeparators("1000000"));
EXPECT_EQ("1,000,000,000", addThousandSeparators("1000000000"));
EXPECT_EQ("1,012,789,345,456,123,678,456,345,809", addThousandSeparators("1012789345456123678456345809"));
}
TEST (addThousandSeparators, negativeBasicNumbers) {
EXPECT_EQ("", addThousandSeparators(""));
EXPECT_EQ("-1", addThousandSeparators("-1"));
EXPECT_EQ("-100", addThousandSeparators("-100"));
EXPECT_EQ("-1,000", addThousandSeparators("-1000"));
EXPECT_EQ("-10,000", addThousandSeparators("-10000"));
EXPECT_EQ("-100,000", addThousandSeparators("-100000"));
EXPECT_EQ("-1,000,000", addThousandSeparators("-1000000"));
EXPECT_EQ("-1,000,000,000", addThousandSeparators("-1000000000"));
EXPECT_EQ("-1,012,789,345,456,123,678,456,345,809", addThousandSeparators("-1012789345456123678456345809"));
}
TEST (addThousandSeparators, changeThousandSeparator) {
EXPECT_EQ("", addThousandSeparators("",'.'));
EXPECT_EQ("-1", addThousandSeparators("-1",'.'));
EXPECT_EQ("100", addThousandSeparators("100", '.'));
EXPECT_EQ("-1.000", addThousandSeparators("-1000", '.'));
EXPECT_EQ("-1.000.000.000", addThousandSeparators("-1000000000", '.'));
EXPECT_EQ("1.012.789.345.456.123.678.456.345.809", addThousandSeparators("1012789345456123678456345809", '.'));
}
TEST (addThousandSeparators, changeDecimalSeparator) {
EXPECT_EQ("0,5", addThousandSeparators("0.5",'.',','));
EXPECT_EQ("0", addThousandSeparators("0",'.',','));
EXPECT_EQ("-1,23", addThousandSeparators("-1.23",'.',','));
EXPECT_EQ("100,56", addThousandSeparators("100.56", '.',','));
EXPECT_EQ("-1.000,786", addThousandSeparators("-1000.786", '.',','));
EXPECT_EQ("-1.000.000.000,4859", addThousandSeparators("-1000000000.4859", '.', ','));
EXPECT_EQ("1.012.789.345.456.123.678.456.345.809,6785960", addThousandSeparators("1012789345456123678456345809.6785960", '.',','));
}
TEST (addThousandSeparators, changeSourceDecimalSeparator) {
EXPECT_EQ("0,5", addThousandSeparators("0,5",'.',',',','));
EXPECT_EQ("0", addThousandSeparators("0",'.',',',','));
EXPECT_EQ("-1,23", addThousandSeparators("-1,23",'.',',',','));
EXPECT_EQ("100,56", addThousandSeparators("100,56", '.',',',','));
EXPECT_EQ("-1.000,786", addThousandSeparators("-1000,786", '.',',',','));
EXPECT_EQ("-1.000.000.000,4859", addThousandSeparators("-1000000000,4859", '.', ',',','));
EXPECT_EQ("1.012.789.345.456.123.678.456.345.809,6785960", addThousandSeparators("1012789345456123678456345809,6785960", '.',',',','));
}
回答4:
Finally, I have developed my own function:
std::string CFormat::GetInteger(int iValue)
{
std::string sValue;
std::string sDot = ".";
for(int iTmp = iValue; iTmp; iTmp /= 1000)
{
if ((int)(iTmp / 1000) == 0)
sDot.clear();
sValue = sDot + SSTR(iTmp % 1000) + sValue;
}
if (sValue.empty())
sValue = "0";
return sValue;
}
I think it's simpler and it does not depend on other classes other than std::string, which I know it will work in Windows Mobile device.
回答5:
Here's another way, using manual manipulations:
#include <iostream>
#include <string>
#include <algorithm>
int main()
{
int number = 123'456'789;
auto src = std::to_string(number);
auto dest = std::string();
auto count = 3;
for(auto i = src.crbegin() ; i != src.crend() ; ++i) {
if (count == 0)
{
dest.push_back(',');
count = 3;
}
if (count--) {
dest.push_back(*i);
}
}
std::reverse(dest.begin(), dest.end());
std::cout << dest << '\n';
}
回答6:
Note: At the time this answer was submitted, the post was tagged C/C++. Now it is tagged C. I suspect it may change again.
Should you want to roll your own C solution which uses C99, the below forms the basis that works on my Windows gcc under various locales.
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#define INT_STR_SIZE (CHAR_BIT*sizeof(int)*3/10 + 2)
#define INT_SEP_STR_SIZE (INT_STR_SIZE * 3/2 + 1)
#define INT_SEP(x) int_sep((char[INT_SEP_STR_SIZE]) { "" }, INT_SEP_STR_SIZE, x)
char *int_sep(char *s, size_t sz, int x) {
struct lconv *locale_ptr = localeconv();
const char *grouping = locale_ptr->grouping;
char sep = locale_ptr->thousands_sep[0];
if (sz > 0) {
int x0 = x;
char *ptr = s + sz;
*--ptr = '\0';
char count = 0;
do {
if (count >= grouping[0]) {
*--ptr = sep;
if (grouping[1]) grouping++;
count = 0;
}
count++;
//printf("%d %d <%s> %p\n", count, n, locale_ptr->grouping, (void*)locale_ptr);
*--ptr = (char) (abs(x % 10) + '0');
} while (x /= 10);
if (x0 < 0) {
*--ptr = '-';
}
memmove(s, ptr, (size_t) (&s[sz] - ptr));
}
return s;
}
main
int main(void) {
puts(setlocale(LC_ALL,"en_US"));
printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_SEP_STR_SIZE), INT_SEP(12345678), INT_SEP(1234));
printf(":%15s: :%15s: :%15s:\n", INT_SEP(-1), INT_SEP(0), INT_SEP(1));
printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
puts(setlocale(LC_ALL,"C"));
printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
puts(setlocale(LC_ALL,"it_IT"));
printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
puts(setlocale(LC_ALL,"as_IN"));
printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
puts(setlocale(LC_ALL,"be_BY"));
printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
return 0;
}
Output
en_US
: 17: : 12,345,678: : 1,234:
: -1: : 0: : 1:
: -2,147,483,648: : -2,147,483,647: : 2,147,483,647:
C
: -2147483648: : -2147483647: : 2147483647:
it_IT
: -2.147.483.648: : -2.147.483.647: : 2.147.483.647:
as_IN
:-2,14,74,83,648: :-2,14,74,83,647: : 2,14,74,83,647:
be_BY
: -2 147 483 648: : -2 147 483 647: : 2 147 483 647:
来源:https://stackoverflow.com/questions/43482488/how-to-format-a-number-with-thousands-separator-in-c-c