番号12343423455.23353
があります。千単位の区切り文字で数値をフォーマットしたい。したがって、出力は12,343,423,455.23353
になります。
$ printf "%'.3f\n" 12345678.901
12,345,678.901
tl; dr
[〜#〜] gnu [〜#〜]の場合、numfmt
を使用しますLinuxなどのユーティリティがデフォルトで利用可能です。
numfmt --grouping 12343423455.23353 # -> 12,343,423,455.23353 in locale en_US
_それ以外の場合、printf
を使用し、_'
_フィールドフラグをシェル関数そのは入力小数点以下の桁数を保持します(出力の数をハードコーディングしません 小数位)。
groupDigits 12343423455.23353 # -> 12,343,423,455.23353 in locale en_US
_groupDigits()
の定義については、この回答の下部を参照してください。これは、複数の入力番号もサポートします。サブシェルを含むアドホックな代替手段も入力小数点以下の桁数を保持します(入力小数点が_.
_または_,
_のいずれかであると想定):
(n=$(</dev/stdin); f=${n#*[.,]}; printf "%'.${#f}f\n" "$n") <<<12343423455.23353
_$n
_を使用するモジュラーの少ない代替手段:n=12343423455.23353; (f=${n#*[.,]} printf "%'.${#f}f\n" "$n")
または、Linux/macOSgrp
CLI(_npm install -g grp-cli
_でインストール可能)の使用を検討してください:
grp -n 12343423455.23353
_すべての場合において、警告;があります。下記参照。
Ignacio Vazquez-Abramsの回答printf
で使用するための重要なポインタが含まれています:_'
_フィールドフラグ(_%
_に続く)は、アクティブなロケールで数値をフォーマットします千の区切り文字:
man printf
_(_man 1 printf
_)には、この情報自体は含まれていないことに注意してください。utility/シェル組み込みprintf
は最終的にlibrary functionprintf()
であり、サポートされている形式に関する全体像を示すのは_man 3 printf
_のみです。LC_NUMERIC
_と、間接的にLANG
または_LC_ALL
_は、数値の書式設定に関してアクティブなロケールを制御します。numfmt
とprintf
はどちらも、千単位の区切り文字と小数点(「小数点」)の両方に関して、アクティブなロケールを尊重します。printf
だけを単独で使用するには、ハードコード出力の数が必要です。入力の小数点以下の桁数を保持するのではなく、小数点以下の桁数。以下のgroupDigits()
が克服するのはこの制限です。printf "%'.<numDecPlaces>f"
_には_numfmt --grouping
_よりも1つの利点がありますが、numfmt
は10進数のみを受け入れますが、printf
の_%f
_は16進数も受け入れます。整数(例:_0x3e8
_)および10進数の数値科学的記数法(例:_1e3
_)。グループ化されていないロケール:一部のロケール、特にC
とPOSIX
は、定義上、グループ化を適用しないため、 _'
_は、そのイベントでは効果がありません。
プラットフォーム間での実際のロケールの不一致:
(LC_ALL='de_DE.UTF-8'; printf "%'.1f\n" 1000) # SHOULD yield: 1.000,0
_1.000,0
_を生成します。1000,0
_-グループ化(!)が発生しません。numfmt
またはprintf
に数値を渡すと、次のようになります:(LC_ALL='lt_LT.UTF-8'; printf "%'.1f\n" 1000,1) # -> '1 000,1'
_Portability:POSIXはrequireprintf
utility (C printf()
library functionとは対照的に)このような浮動小数点形式の文字をサポートしますPOSIX [-like]シェルが整数のみである場合、_%f
_として。ただし、実際には、そうでないシェル/プラットフォームはありません。
丸め誤差とオーバーフロー:
numfmt
とprintf
を使用すると、丸め誤差が発生するラウンドトリップ変換が発生します(文字列->数値->文字列)。言い換えると、数字のグループ化を使用して再フォーマットすると、異なる数値になる可能性があります。f
を使用して IEEE-754倍精度浮動小数点値 、最大15 有効数字のみ=(小数点の位置に関係なく数字)は正確に保存されることが保証されています(ただし、特定の数字の場合は、より多くの数字で機能する可能性があります) )。 実際には、numfmt
と[〜#〜] gnu [〜#〜]printf
はmoreを正確に処理できますそれより;下記参照。誰かがその方法と理由を知っているなら、私に知らせてください。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
_
対照的に、大きすぎる数はデフォルトでエラーを生成します。
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
_
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
}
_