Set thousands separator for C printf

I have this C code:

locale_t myLocale = newlocale(LC_NUMERIC_MASK, "en_US", (locale_t) 0); uselocale(myLocale); ptrLocale = localeconv(); ptrLocale->thousands_sep = (char *) "'"; int i1 = snprintf( s1, sizeof(s1), "%'d", 123456789); 

The output in s1 is 123,456,789 .

Even if I set ->thousands_sep to ' , it is ignored. Is there a way to set any character as a thousands separator?

+6
source share
4 answers

Here is a very simple solution that works on every Linux distribution and does not need my first answer - a glibc hack:


All of these steps must be performed in the glibc origin directory β€” NOT in the build directory β€” after you build the glibc version using a separate build directory, as suggested by these instructions .

My new locale file is called en_AT .

  • Create a new en_AT file in the localedata/locales/ directory from the existing en_US file.
  • Change all entries for thousands_sep to thousands_sep "<U0027>" or any other character you want to use as the thousands separator.
  • Change all occurrences of en_US to en_AT inside the new file.
  • Add the line: en_AT.UTF-8/UTF-8 \ localedata/SUPPORTED file.
  • Run in the build make localedata/install-locales directory.
  • The new locale will be automatically added to the system and available instantly for the program.

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

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

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

1'000'000


Note: If you need a program that defines different localizations that are defined at runtime, you can use this example in the man pages where you load the requested locale and simply change the LC_NUMERIC settings from en_AT .

0
source

The localeconv() function just read the localization settings, and ptrLocale->thousands_sep itself will not change the settings for the current locale.

EDIT:

I do not know how to do this in C, but you can find many examples with C ++ output. 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:

C ++ link:

localeconv () returns a pointer to a populated 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 ​​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 this is a bad and insecure 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 changes to decimal_point and thousands_sep possible using the pointer obtained 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* } 
+3
source

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

  • Download GNU libc.
  • run configure --prefix=/usr/glibc-version
  • run make -j 8
  • get a very long compiler command with all the switches from make output
  • write the C source file setMyThousandSeparator.c - see contents below
  • compile this source file using the gcc switches from step 3.
  • in your normal C source code request setMyThousandSeparator("'") before calling printf() .
  • link setMyThousandSeparator.o with your project.

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

Contents 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 accesses the same data that printf() does!

+2
source

This answer is from VolAnd one .

According to this source , the thousands separator is used only with a non-standard flag. '

So, if your printf compatible with POSIX.1-2008, you can use:

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

Source: https://habr.com/ru/post/983045/


All Articles