Make gzprintf ignore locale LC_NUMERIC

Viewed 131

How can I make gzprintf (or printf in general) to use "." as decimal separator, effectively ignoring LC_NUMERIC or using "LC_NUMERIC = C".

Background: My program which uses gettext , does setlocale (LC_ALL, ....) to change it's locale. But at one point I need to write to a measurement file which should have a fixed "." as decimal separator. Of course I can do something like

const char *old = setlocale (LC_NUMERIC, NULL);
... make copy
setlocale (LC_NUMERIC, "C");
... use gzprintf
setlocale (LC_NUMERIC, old_copy);

but I fear runtime problems because I have to write samples with 2kHz to the file but haven't benchmarked it yet.

1 Answers

Not only is wrapping each call with setlocale like that potentially slow. It's completely thread-unsafe and thus unsafe to use in general/library code.

Assuming you're on a POSIX or POSIX-like system, what you want is newlocale/uselocale. Perform the following (which may be slow) once and save the result:

locale_t safe_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));

Then, whenever you want to use it:

locale_t old = uselocale(safe_locale);
/* Do stuff with it. */
uselocale(old);

You need not switch back and forth around each operation, only before returning to a caller that might expect its locale to be undisturbed, but it should not be slow to switch back and forth like this anyway.

Alternatively, just don't honor LC_NUMERIC in your application at all. After calling setlocale(LC_ALL, "") to setup the locale according to the user's settings, call setlocale(LC_NUMERIC, "C") and leave it that way. This is not valid/acceptable in library code, but if you're writing library code, you can punt and let the caller take responsibility for doing whichever approach it finds appropriate (never honoring LC_NUMERIC at all, or using newlocale/uselocale to switch). You can either just write this requirement in the documentation of its contract, and treat failure to honor the contract as undefined, or you can actively test for non-'.' radix point by asserting that localeconv()->decimal_point points to a string "." (and returning an error or aborting if that fails to hold).

Related