cup cup - 2 months ago 10
C++ Question

C# equivalent of imbue and numpunct

I'm trying to find the C# equivalent of the following in C++. This is purely for fun

#include <iomanip>
#include <iostream>
#include <tchar.h>

class CommaSep : public std::numpunct<TCHAR>
{
public:
CommaSep(TCHAR thousands_sep, const char* grouping)
:m_thousands_sep(thousands_sep),
m_grouping(grouping){}
protected:
TCHAR do_thousands_sep() const{return m_thousands_sep;}
std::string do_grouping() const {return m_grouping;}
private:
TCHAR m_thousands_sep;
std::string m_grouping;
};

int main()
{
double number = 1234567.1235;
std::locale comma_locale(std::locale(), new CommaSep(_T(','), "\03"));
std::locale indian_locale(std::locale(), new CommaSep(_T(','), "\02\02\03"));
std::locale weird_locale(std::locale(), new CommaSep(_T(','), "\02\01\03"));

std::wcout.imbue(comma_locale);
std::wcout << std::setprecision(2) << std::fixed << number << std::endl;
std::wcout.imbue(indian_locale);
std::wcout << std::setprecision(2) << std::fixed << number << std::endl;
std::wcout.imbue(weird_locale);
std::wcout << std::setprecision(2) << std::fixed << number << std::endl;
}


The result is

1,234,567.12
123,45,67.12
1,234,5,67.12


It does exactly what I tell it. I tried the following in C#. The code is a lot smaller (hooray) but the compiler just ignores the formatting and does its own thing (boo hiss)

namespace imbuecs
{
class Imbue
{
static void Main(string[] args)
{
int bignum = 123456789;

System.Console.WriteLine(bignum.ToString("########0"));
// Indian
System.Console.WriteLine(bignum.ToString("##,###,##,#0"));
System.Console.WriteLine(bignum.ToString("###,###,##0"));
// Chinese
System.Console.WriteLine(bignum.ToString("#,####,###0"));
}
}
}


What I got was

123456789
123,456,789 expected 12,345,67,89
123,456,789
123,456,789 expected 1,2345,6789


I can write huge swathes of code for doing this but I'm wondering if there is a simple way of doing this.

Answer

From the documentation, when specifiying custom formatting, the comma (emphasis mine):

Serves as both a group separator and a number scaling specifier. As a group separator, it inserts a localized group separator character between each group. As a number scaling specifier, it divides a number by 1000 for each comma specified.

So it causes the number to be grouped into 3 digits. If you want to make your own format, you need to enclose the comma in single quotes to render it as a literal string. For example:

Console.WriteLine(bignum.ToString("##','###','##','#0"));
Console.WriteLine(bignum.ToString("#','####','###0"));

Will output:

12,345,67,89
1,2345,6789

Another alternative is to use the NumberFormatInfo object with the NumberGroupSizes property. For example:

var nfi = new System.Globalization.NumberFormatInfo();
//First group is 2 digits, the rest are 3 digits
nfi.NumberGroupSizes = new int[] { 2, 3 }; 

Console.WriteLine(bignum.ToString("#,#0", nfi));