Set thousands separator for C printf

Solution 1:

Here is a very simple solution which works on each linux distribution and does not need - as my 1st answer - a glibc hack:


All these steps must be performed in the origin glibc directory - NOT in the build directory - after you built the glibc version using a separate build directory as suggested by this instructions.

My new locale file is called en_AT.

  1. Create in the localedata/locales/ directory from an existing file en_US a new file en_AT .
  2. Change all entries for thousands_sep to thousands_sep "<U0027>" or whatever character you want to have as the thousands separator.
  3. Change inside of the new file all occurrences of en_US to en_AT.
  4. Add to the file localedata/SUPPORTED the line: en_AT.UTF-8/UTF-8 \.
  5. Run in the build directory make localedata/install-locales.
  6. The new locale will be then automatically added to the system and is instantly accessible for the program.

In the C/C++ program you switch to the new thousands separator character with:

setlocale( LC_ALL, "en_AT.UTF-8" );

using it with printf( "%'d", 1000000 ); which produces this output

1'000'000


Remark: When you need in the program different localizations which are determinated while the runtime you can use this example from the man pages where you load the requested locale and just replace the LC_NUMERIC settings from en_AT.

Solution 2:

Function localeconv() just read locate settings, and ptrLocale->thousands_sep itself not changes that settings for current locale.

EDIT:

I do not know how to do this in C, but lots of examples with C++ output can be found. See the following example in C++:

#include <iostream>
#include <locale>
using namespace std;

struct myseps : numpunct<char> { 
   // use ' as separator
   char do_thousands_sep() const { return '\''; } 

   // digits are grouped by 3
   string do_grouping() const { return "\3"; }
};

int main() {
  cout.imbue(locale(locale(), new myseps));
  cout << 1234567; // the result will be 1'234'567
}

EDIT 2:

The C++ reference said:

localeconv() returns a pointer to a filled-in object of type struct lconv. The values contained in the object can be overwritten by subsequent calls to localeconv and do not directly modify the object. Calls to setlocale with category values of LC_ALL, LC_MONETARY, or LC_NUMERIC overwrite the contents of the structure.

I tried the following example in MS Visual Studio 2012 (I understand that it is bad and unsafe style):

#include <stdio.h>
#include <locale.h>
#include <string.h>

int main() {
    setlocale(LC_NUMERIC, "");
    struct lconv *ptrLocale = localeconv();
    strcpy(ptrLocale->decimal_point, ":");
    strcpy(ptrLocale->thousands_sep, "'");
    char str[20];
    printf("%10.3lf \n", 13000.26);
    return 0;
}

and I saw the result:

  13000:260

therefore, it can be assumed that the changes of decimal_point and thousands_sep are possible through pointer received with localeconv(), but printf ignores thousands_sep.

EDIT 3:

Updated C++ example:

#include <iostream>
#include <locale>
#include <sstream>
using namespace std;

struct myseps : numpunct<char> { 
   // use ' as separator
   char do_thousands_sep() const { return '\''; } 

   // digits are grouped by 3
   string do_grouping() const { return "\3"; }
};

int main() {
  stringstream ss;
  ss.imbue(locale(locale(), new myseps));
  ss << 1234567;  // printing to string stream with formating
  printf("%s\n", ss.str().c_str()); // just output when ss.str() provide string, and c_str() converts it to char*
}

Solution 3:

There is a really very dirty hack how to change the thousand separator character for printf():

  1. Download the GNU libc.
  2. run the configure --prefix=/usr/glibc-version command
  3. run make -j 8
  4. get the very long compiler command with all switches from the make output
  5. write the C source file setMyThousandSeparator.c - content see below
  6. compile this source file with the gcc switches from point 3.
  7. in your normal C source code call setMyThousandSeparator("'") function before the printf() call.
  8. link setMyThousandSeparator.o with your project.

For the moment I tried it when linking libc static but it works.

Content of setMyThousandSeparator.c:

#include <locale/localeinfo.h>

void setMyThousandSeparator(char * sMySeparator)
{
    _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP) = sMySeparator;
}

Info: This solution is thread safe because it is accessing the same data as printf() does!