Does std::locale::global make affect to printf() f

2019-02-17 00:20发布

问题:

I'm using Russian locale settings on my PC.
If I would set:

class numpunct_withpoint: public numpunct<char>
{
protected:
    /// Override the function that gives the decimal separator.
    char do_decimal_point() const
    {
        return '.';
    }
};
...
locale loc(std::locale::classic(), new numpunct_withpoint);
std::locale::global(loc);


and than

printf("%f", 3.14);

Output is:

3,14

Decimal separator is "," not like in do_decimal_point function! How can I change C locale settings for decimal separator with C++ locale?


Thanks for answer!
But, does C setlocale function make affect to C++ cout object?

setlocale(LC_NUMERIC, "C");

Would this make affect when printing decimal value via cout?

回答1:

When you set the global C++ locale the C locale is modified as well. If you modify the C locale the global C++ locale is not modified.

The following demonstrates setting the C++ global locale.

#include <cstdio>
#include <clocale>
#include <fstream>

int main() {
    const char * locale_name = "French_France.1252"; // or "fr_Fr.UTF-8" on Unix
    double value = 1.2;

    std::locale::global(std::locale(locale_name));

    std::ofstream("out.txt") << "C++ " << value << '\n';

    if (FILE *f = std::fopen("out.txt", "a")) {
        std::fprintf(f, "C %1.1e\n", value);
        std::fclose(f);
    }
}

Both C and C++ output should use a comma decimal point.

C++ 1,2
C 1,2e+000

If you replace setting the C++ locale with setting the C locale, std::setlocale(LC_ALL, locale_name);, then the output should change so that only the C output uses a comma decimal while the C++ output still uses the default period decimal symbol.

C++ 1.2
C 1,2e+000

However, the fact that setting the C++ locale affects the C locale does not make C locales extensible in the way that C++ locales are. Custom C++ facets will never be used by the C locale based functions. Instead you must rely on your system supporting some named locale that has the functionality you need.

Specifically, std::locale::global() is defined to set the C locale using the name of the C++ locale you choose, if it has a name. The behavior is implementation defined if the C++ locale does not have a name. Also, C++ specifies that combining two named locales produces a named locale. Some implementations produce useful combined names that allow you mix C locale categories just by setting the C++ locale:

std::locale::global(std::locale(std::locale("ru_RU"), "C", std::locale::numeric));

With libstdc++ this produces a locale named:

LC_CTYPE=ru_RU;LC_NUMERIC=C;LC_TIME=ru_RU;LC_COLLATE=ru_RU;LC_MONETARY=ru_RU;LC_MESSAGES=ru_RU;LC_PAPER=ru_RU;LC_NAME=ru_RU;LC_ADDRESS=ru_RU;LC_TELEPHONE=ru_RU;LC_MEASUREMENT=ru_RU;LC_IDENTIFICATION=ru_RU LC_CTYPE=ru_RU;LC_NUMERIC=C;LC_TIME=ru_RU;LC_COLLATE=ru_RU;LC_MONETARY=ru_RU;LC_MESSAGES=ru_RU;LC_PAPER=ru_RU;LC_NAME=ru_RU;LC_ADDRESS=ru_RU;LC_TELEPHONE=ru_RU;LC_MEASUREMENT=ru_RU;LC_IDENTIFICATION=ru_RU

So that the C locale is set to the same mixture between the "ru_RU" and "C" locales as the C++ locale.

Unfortunately other implementations choose less useful, though still technically conformant, behavior. In Visual Studio

std::locale::global(std::locale(std::locale("Russian_Russia.1251"), "C", std::locale::numeric));

produces a locale with the name "C". So while the C++ locale is the appropriate mixture of Russian and C locale categories, the C locale is simply set to "C". So on these platforms to mix C locale categories you must directly set the C locale:

// set the C++ locale first
std::locale::global(std::locale(std::locale("Russian_Russia.1251"), "C", std::locale::numeric));

// set the C locale second, because it will not overwrite the changes you made to the C++ locale
std::setlocale(LC_ALL, "Russian_Russia.1251");
std::setlocale(LC_NUMERIC, "C");


回答2:

The C library does not use the same locale settings as C++. To override the locale used by printf, use setlocale:

setlocale(LC_NUMERIC, "POSIX");

or similar.