BASH中的数字格式与千位分隔符

时间:2012-02-21 09:15:43

标签: bash unix localization number-formatting

我有一个号码12343423455.23353。我想用千位分隔符格式化数字。所以输出将是12,343,423,455.23353

2 个答案:

答案 0 :(得分:33)

$ printf "%'.3f\n" 12345678.901
12,345,678.901

答案 1 :(得分:8)

<强> 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_US
    • 请参阅本答案的底部,了解groupDigits()的定义,该定义还支持多个输入数字。
  • 涉及子广告的特殊替代方案 还保留输入小数位数(假设输入小数点是.,):

    • 一种模块化但有些低效的变体,它通过 stdin 接受输入编号(因此也可以与管道输入一起使用):
      (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 grp CLI (可以npm install -g grp-cli安装):

    • grp -n 12343423455.23353

在所有情况下都有警告;见下文。

Ignacio Vazquez-Abrams's answer包含与printf一起使用的关键指针:'字段标记(在%之后)格式化带有活动区域设置的数字&#39; s分离器:

  • 请注意,man printfman 1 printf)本身不包含此信息:实用程序 / shell builtin printf最终调用库函数< / em> printf(),只有man 3 printf能够全面了解支持的格式。
  • 环境变量LC_NUMERIC和间接LANGLC_ALL控制有关数字格式的活动区域设置。
  • numfmtprintf都尊重活动区域设置,包括千位分隔符和小数点(&#34;小数点&#34;)。
  • 单独使用printf,就像在Ignacio的回答中一样,要求您硬编码 输出小数位数,而不是比保留输入有多少小数位;这是groupDigits()以下克服的限制。
  • printf "%'.<numDecPlaces>f"确实比numfmt --grouping有一个优势,但是:
    • numfmt仅接受十进制号码,而printf&#39; s %f也接受十六进制整数(例如, 0x3e8)和十进制数字科学记数法(例如1e3)。

注意事项

  • 没有分组的区域设置:根据定义,某些区域设置(尤其是CPOSIX不应用分组,因此使用'没有在那种情况下的效果。

  • 跨平台的实际区域设置不一致

    • (LC_ALL='de_DE.UTF-8'; printf "%'.1f\n" 1000) # SHOULD yield: 1.000,0
    • Linux :按预期收取1.000,0
    • macOS / BSD :意外收获1000,0 - 没有分组(!)。
  • 输入数字格式:当您将号码传递给numfmtprintf时,它会:
    • 不得已包含数字分组
    • 必须已使用有效区域设置的小数点
    • 例如:
      • (LC_ALL='lt_LT.UTF-8'; printf "%'.1f\n" 1000,1) # -> '1 000,1'
      • 确定:输入数字未分组,并使用立陶宛小数点(逗号)。
  • 可移植性:POSIX不要求 printf utility(而不是C printf() 库函数)支持浮点格式字符,例如%f,因为POSIX [-like] shell只是整数;但实际上,我并不知道任何没有的shell /平台。

  • 舍入错误和溢出

    • 如上所述使用numfmtprintf时,会发生往返转换(字符串 - >数字 - >字符串),这会导致舍入错误;换句话说:使用数字分组重新格式化会导致数字不同
    • 使用格式字符f来使用IEEE-754 double-precision floating-point values,只有最多15 significant digits (与小数点位置无关的数字)保证被准确保存(尽管对于特定数字,它可以使用更多数字)。 在实践中,numfmt GNU printf可以准确处理更多;见下文。如果有人知道如何以及为什么,请告诉我。
    • 如果有大量有效数字或值过大,行为通常会在numfmtprintf 之间有所不同,而{strong}之间的会有所不同跨平台实施;例如:

<强> printf

[已在coreutils 8.24中修复,根据@pixelbeat] 从20位有效数字开始,值溢出(!) - 可能是一个错误(从GNU coreutils 8.23开始):< / p>

numft

相比之下,过大的数字会默认生成错误。

<强> # 20 significant digits cause quiet overflow: $ (fractPart=0000000000567890; num="1000.${fractPart}"; numfmt --grouping "$num") -92.23372036854775807 # QUIET OVERFLOW

Linux printf最多可处理20位有效数字 ,而BSD / macOS实施仅限于17:

printf

Linux版本似乎永远不会溢出,而BSD / macOS版本报告错误的数字太大。

Bash shell函数# 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

groupDigits()