Shi*_*dim 17 unix bash localization number-formatting
我有一个号码12343423455.23353.我想用千位分隔符格式化数字.所以输出就是 12,343,423,455.23353
Ign*_*ams 35
$ printf "%'.3f\n" 12345678.901
12,345,678.901
Run Code Online (Sandbox Code Playgroud)
TL;博士
使用numfmt,如果GNU实用程序可用,比如Linux上的默认:
numfmt --grouping 12343423455.23353 # -> 12,343,423,455.23353 in locale en_US否则,使用printf包含'在shell函数中的字段标志,该函数保留输入小数位数(不会硬编码输出小数位数).
groupDigits 12343423455.23353 # -> 12,343,423,455.23353 in locale en_USgroupDigits()还支持多个输入数字.特设的替代品,涉及子shell是还保留的输入小数位数(假定输入小数点符号或者是.或,):
(n=$(</dev/stdin); f=${n#*[.,]}; printf "%'.${#f}f\n" "$n") <<<12343423455.23353$n: n=12343423455.23353; (f=${n#*[.,]} printf "%'.${#f}f\n" "$n")或者,考虑使用我的Linux/macOS grpCLI(可安装npm install -g grp-cli):
grp -n 12343423455.23353在所有情况下都有警告 ; 见下文.
Ignacio Vazquez-Abrams的答案包含用于的关键指针printf:'字段标志(跟随%)格式化一个带有活动区域设置的千位分隔符的数字:
man printf(man 1 printf)本身不包含此信息:实用程序/shell内置printf最终调用库函数 printf(),并且仅man 3 printf提供有关支持格式的完整图片.LC_NUMERIC并间接地LANG或LC_ALL控制与数字格式相关的活动区域设置.numfmt和printf尊重有源区域设置,两者相对于所述千位分隔和十进制标记("小数点").printf本身,作为伊格纳西奥的答案,需要你硬编码的数字输出小数,而不是保留然而,许多小数输入了; 这是groupDigits()下面克服的限制.printf "%'.<numDecPlaces>f"确实有一个优势numfmt --grouping,但是:
numfmt只接受十进制数,而printf's %f也接受十六进制整数(例如0x3e8)和十进制科学数字表示的数字(例如1e3).不进行分组区域设置:某些地区,特别是C和POSIX,顾名思义不要申请分组,所以使用'在该事件没有影响.
跨平台的实际区域设置不一致:
(LC_ALL='de_DE.UTF-8'; printf "%'.1f\n" 1000) # SHOULD yield: 1.000,01.000,0正如预期的那样收益率.1000,0- 没有分组(!).numfmt或时printf,它:
(LC_ALL='lt_LT.UTF-8'; printf "%'.1f\n" 1000,1) # -> '1 000,1'可移植性:POSIX不要求的printf 实用程序(如相对于在C printf() 库函数),以支持浮点格式的字符,如%f,假定POSIX [样]壳是整数仅; 但实际上,我并不知道任何没有的shell /平台.
舍入错误和溢出:
numfmt和printf如上所述时,发生往返转换(字符串 - >数字 - >字符串),这可能会出现舍入错误; 换句话说:使用数字分组重新格式化可能会导致不同的数字.f采用IEEE-754双精度浮点值,只有最多15 显著位(不考虑小数点标记的位置的位)都保证要保持精度(尽管具体数字可能有更多的数字工作).在实践中,numfmt并且GNU printf可以精确地处理更多的比; 见下文.如果有人知道如何以及为什么,请告诉我.numfmt并且printf在一般情况下,和之间printf跨平台的实现 ; 例如:numft:
[修正了coreutils 8.24,根据@pixelbeat ]从20位有效数字开始,值溢出(!) - 可能是一个错误(从GNU coreutils 8.23开始):
# 20 significant digits cause quiet overflow:
$ (fractPart=0000000000567890; num="1000.${fractPart}"; numfmt --grouping "$num")
-92.23372036854775807 # QUIET OVERFLOW
Run Code Online (Sandbox Code Playgroud)
相反,默认情况下,数字太大会产生错误.
printf:
Linux 准确printf处理多达20位有效数字,而BSD/macOS实现仅限于17:
# Linux: 21 significant digits cause rounding error:
$ (fractPart=00000000005678901; num="1000.${fractPart}"; printf "%'.${#fractPart}f\n" "$num")
1,000.00000000005678902 # ROUNDING ERROR
# BSD/macOS: 18 significant digits cause rounding error:
$ (fractPart=00000000005678; num="1000.${fractPart}"; printf "%'.${#fractPart}f\n" "$num")
1,000.00000000005673 # ROUNDING ERROR
Run Code Online (Sandbox Code Playgroud)
Linux版本似乎永远不会溢出,而BSD/macOS版本报告错误的数字太大.
groupDigits():# SYNOPSIS
# groupDigits num ...
# DESCRIPTION
# Formats the specified number(s) according to the rules of the
# current locale in terms of digit grouping (thousands separators).
# Note that input numbers
# - must not already be digit-grouped themselves,
# - must use the *current* locale's decimal mark.
# Numbers can be integers or floats.
# Processing stops at the first number that can't be formatted, and a
# non-zero exit code is returned.
# CAVEATS
# - No input validation is performed.
# - printf(1) is not guaranteed to support non-integer formats by POSIX,
# though not doing so is rare these days.
# - Round-trip number conversion is involved (string > double > string)
# so rounding errors can occur.
# EXAMPLES
# groupDigits 1000 # -> '1,000'
# groupDigits 1000.5 # -> '1,000.5'
# (LC_ALL=lt_LT.UTF-8; groupDigits 1000,5) # -> '1 000,5'
groupDigits() {
local decimalMark fractPart
decimalMark=$(printf "%.1f" 0); decimalMark=${decimalMark:1:1}
for num; do
fractPart=${num##*${decimalMark}}; [[ "$num" == "$fractPart" ]] && fractPart=''
printf "%'.${#fractPart}f\n" "$num" || return
done
}
Run Code Online (Sandbox Code Playgroud)