BASH 中的数字格式与千位分隔符
我有一个号码 12343423455.23353
。我想用千位分隔符格式化数字。所以输出将是 12,343,423,455.23353
I have a number 12343423455.23353
. I want to format the number with thousand separator. So th output would be 12,343,423,455.23353
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
tl;dr
使用
numfmt
,如果 GNU 实用程序可用,例如默认在 Linux 上:numfmt --grouping 12343423455.23353 # -> 12,343,423,455.23353(区域设置 en_US)
否则,使用
printf
以及封装在 shell 函数中的'
字段标志< /strong> 保留输入小数位数(不会对输出小数位数进行硬编码)。groupDigits 12343423455.23353 # -> 12,343,423,455.23353(区域设置 en_US)
groupDigits()
的定义,它还支持多个输入数字。涉及子 shell 的临时替代方案,还保留输入小数位数(假设输入小数标记为
.
或<代码>,):(n=$(
$n
的速度明显更快,但模块化程度较低的替代方案:n=12343423455.23353; (f=${n#*[.,]} printf "%'.${#f}f\n" "$n")
或者,考虑使用我的 Linux/macOS
grp
CLI (可使用npm 安装安装-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
都尊重活动区域设置,无论是千位分隔符还是小数点(“小数点”)。groupDigits()
正是克服了这个限制。printf "%'.f"
确实比numfmt --grouping
有一个优势:numfmt
仅接受十进制 数字,而printf
的%f
也接受十六进制< /em> 整数(例如,0x3e8
)和十进制数科学记数法(例如,1e3
)。注意事项
不进行分组的区域设置:某些区域设置,特别是
C
和POSIX
,根据定义不应用分组,因此使用'
在该事件中无效。跨平台的实际区域设置不一致:
(LC_ALL='de_DE.UTF-8'; printf "%'.1f\n" 1000) # 应输出:1.000,0
1.000,0
,如预期。1000,0
- 没有分组(!)。numfmt
或printf
时,它:(LC_ALL='lt_LT.UTF-8'; printf "%'.1f\n" 1000,1) # -> '1 000,1'
可移植性:POSIX 不需要
printf
实用程序(与 Cprintf()
库相反function)来支持浮点格式字符,例如%f
,假设 POSIX[-like] shell 仅支持整数;然而,在实践中,我不知道有任何 shell/平台不这样做。舍入错误和溢出:
numfmt
和printf
时,会发生往返转换(字符串 -> 数字 -> 字符串),这会产生舍入错误;换句话说:使用数字分组重新格式化可能会导致不同的数字。f
来采用IEEE-754双精度精度浮点值,仅最多 15 显着数字(无论小数点位置如何的数字)保证被准确保留(尽管对于特定数字,它可能适用于更多数字)。 实际上,numfmt
和 GNUprintf
可以准确地处理更多;见下文。如果有人知道如何以及为什么,请告诉我。numfmt
和printf
之间的行为通常有所不同,并且 >跨平台的printf
实现;例如:numft
:[已在 coreutils 8.24 中修复,根据 @pixelbeat] 从 20 个有效数字开始,该值会悄悄溢出(!) - 可能是一个错误(从 GNU coreutils 8.23 开始):
相比之下,数字太大默认情况下确实生成错误。
printf
:Linux
printf
可以准确处理最多 20 位有效数字,而 BSD/macOS 实现仅限于 17 位:Linux 版本似乎永远不会溢出,而 BSD/macOS 版本则报告数字太大的错误。
Bash shell 函数
groupDigits()
:tl;dr
Use
numfmt
, if GNU utilities are available, such as on Linux by default:numfmt --grouping 12343423455.23353 # -> 12,343,423,455.23353 in locale en_US
Otherwise, use
printf
with the'
field flag wrapped in a shell function that preserves the number of input decimal places (does not hard-code the number of output decimal places).groupDigits 12343423455.23353 # -> 12,343,423,455.23353 in locale en_US
groupDigits()
, which also supports multiple input numbers.Ad-hoc alternatives involving subshells that also preserve the number of input decimal places (assumes that the input decimal mark is either
.
or,
):(n=$(</dev/stdin); f=${n#*[.,]}; printf "%'.${#f}f\n" "$n") <<<12343423455.23353
$n
:n=12343423455.23353; (f=${n#*[.,]} printf "%'.${#f}f\n" "$n")
Alternatively, consider use of my Linux/macOS
grp
CLI (installable withnpm install -g grp-cli
):grp -n 12343423455.23353
In all cases there are caveats; see below.
Ignacio Vazquez-Abrams's answer contains the crucial pointer for use with
printf
: the'
field flag (following the%
) formats a number with the active locale's thousand separator:man printf
(man 1 printf
) does not contain this information itself: the utility / shell builtinprintf
ultimately calls the library functionprintf()
, and onlyman 3 printf
gives the full picture with respect to supported formats.LC_NUMERIC
and, indirectly,LANG
orLC_ALL
control the active locale with respect to number formatting.numfmt
andprintf
respect the active locale, both with respect to the thousands separator and the decimal mark ("decimal point").printf
by itself, as in Ignacio's answer, requires that you hard-code the number of output decimal places, rather than preserving however many decimal places the input has; it is this limitation thatgroupDigits()
below overcomes.printf "%'.<numDecPlaces>f"
does have one advantage overnumfmt --grouping
, however:numfmt
only accepts decimal numbers, whereasprintf
's%f
also accepts hexadecimal integers (e.g.,0x3e8
) and numbers in decimal scientific notation (e.g.,1e3
).Caveats
Locales without grouping: Some locales, notably
C
andPOSIX
, by definition do NOT apply grouping, so use of'
has no effect in that event.Real-world locale inconsistencies across platforms:
(LC_ALL='de_DE.UTF-8'; printf "%'.1f\n" 1000) # SHOULD yield: 1.000,0
1.000,0
, as expected.1000,0
- NO grouping(!).numfmt
orprintf
, it:(LC_ALL='lt_LT.UTF-8'; printf "%'.1f\n" 1000,1) # -> '1 000,1'
Portability: POSIX doesn't require the
printf
utility (as opposed to the Cprintf()
library function) to support floating-point format characters such as%f
, given that POSIX[-like] shells are integer-only; in practice, however, I'm not aware of any shells/platforms that do not.Rounding errors and overflow:
numfmt
andprintf
as described, round-trip conversion occurs (string -> number -> string), which is subject to rounding errors; in other words: reformatting with digit grouping can lead to a different number.f
to employ IEEE-754 double-precision floating-point values, only up to 15 significant digits (digits irrespective of the location of the decimal mark) are guaranteed to be accurately preserved (though for specific numbers it may work with more digits). In practice,numfmt
and GNUprintf
can accurately handle more than that; see below. If anyone knows how and why, let me know.numfmt
andprintf
in general, and betweenprintf
implementations across platforms; for example:numft
:[Fixed in coreutils 8.24, according to @pixelbeat] Starting with 20 significant digits, the value overflows quietly(!) - presumably a bug (as of GNU coreutils 8.23):
By contrast, a number that is too large does generate an error by default.
printf
:Linux
printf
handles up to 20 significant digits accurately, whereas the BSD/macOS implementation is limited to 17:The Linux version never seems to overflow, whereas the BSD/macOS version reports an error with numbers that are too large.
Bash shell function
groupDigits()
: