Home > Enterprise >  How to determine which character is used as decimal separator or thousand separator under current lo
How to determine which character is used as decimal separator or thousand separator under current lo

Time:06-30

Comming from this answer to Format currency in Bash, I wonder for ways to determine which characters are used as numeric separators.

There are lot of issue regarding locales and number formating, for sample:

printf '%.5f\n' $(bc -l <<<'4*a(1)')
3.14159

LANG=de_DE printf '%.5f\n' $(bc -l <<<'4*a(1)')
bash: printf: 3.14159265358979323844: invalid number
3,00000

binary calculator bc seem not handling locale correctly...

Under mentioned answer, searching for decimal separator (or radix character), I've used this:

int2amount() {
    local TIMEFORMAT=%U _decsep
    read _decsep < <(eval 'time true' 2>&1)
    _decsep=${_decsep//[0-9]}
    ...
}

This work fine:

pi() { local TIMEFORMAT=%U _decsep;read _decsep < <(eval 'time true' 2>&1);_decsep=${_decsep//[0-9]};
       local pi=$(bc -l <<<'4*a(1)')
       printf '%.5f\n' ${pi/./$_decsep}
}

pi
3.14159
LANG=de_DE pi
3,14159

But as thousand separator is a lot easier to find:

printf -v ts "%'d" 1111 ; ts=${ts//1}

There is no fork, so system footprint is very light.

So I could imagine at begin of source file, something like:

numericSeparators() {
    local TIMEFORMAT=%U
    read NUM_DEC_SEP < <(eval 'time true' 2>&1)
    NUM_DEC_SEP=${NUM_DEC_SEP//[0-9]}
    printf -v NUM_THO_SEP "%'d" 1111
    NUM_THO_SEP=${NUM_THO_SEP//1}
}
numericSeparators
declare -r NUM_THO_SEP NUM_DEC_SEP
...

But I think <(eval 'time true' 2>&1) heavy for the goal. I'm searching for a lighter and/or cleaner way for determine them (even both decimal and thousand separators).


Thanks to dan's answer, my function would become simplier and quicker!

numericSeparators() {
    local numtest
    printf -v numtest "%'.1f" 1111
    NUM_THO_SEP=${numtest:1:1}
    NUM_THO_SEP=${NUM_THO_SEP/1}
    NUM_DEC_SEP=${numtest: -2:1}
}
numericSeparators
for loctest in   C   en_US.UTF-8   de_DE.UTF-8   ;do
    LANG=$loctest numericSeparators
    printf '  %-12s thousand: \47%s\47 decimal: \47%s\47\n' \
            "$loctest"  "$NUM_DEC_SEP"  "$NUM_THO_SEP"
done

  C            thousand: '.' decimal: ''
  en_US.UTF-8  thousand: '.' decimal: ','
  de_DE.UTF-8  thousand: ',' decimal: '.'

CodePudding user response:

You can get the locale's radix character (decimal separator) with:

printf -v ds '%#.1f' 1
ds=${ds//[0-9]}

And the thousands grouping separator, with:

printf -v ts "%'d" 1111
ts=${ts//1}

Some locales (eg. C) have no thousands separator, in which case $ts is empty. Conversely, if the radix character is not defined by the locale, POSIX (printf(3)) says it should default to .. The # flag guarantees that it will be printed.

  • Related