為人類格式化美觀的數字
在 Crystal 0.28.0 版本中,我們有一個新功能可以為人類讀者格式化數字。
以前的選項是在各種 Number
類型上使用 #to_s
,或者頂多使用 sprintf
。兩者都只提供有限的輸出格式,並且它們的重點在於如何為電腦表示數字。它們並未考慮到人類的可讀性。
當在使用者介面中顯示數字時,它們需要讓人類讀者能夠理解。
格式化數字
認識新的 Number#format
方法。
它允許以可自訂的格式列印數字,可以表示人類通常書寫數字的方式。
數字樣式
可以使用可配置的小數分隔符和千位分隔符來格式化數字
123_456.789.format('.', ',') # => "123,456.789"
123_456.789.format(',', '.') # => "123.456,789"
123_456.789.format(',', ' ') # => "123 456,789"
123_456.789.format(',', '\'') # => "123'456,789"
千位組中的位數也是可配置的。例如,這適用於以萬為單位分組的中文數字
123_456.789.format('.', ',', group: 4) # => "12,3456.789"
在不同的文化背景下使用了許多不同的樣式,而此方法足夠靈活,可以表示大多數常見格式。
世界如何分隔其數字概述了國際樣式,而關於小數分隔符的維基百科文章提供了有關此主題的更多見解。
小數位數
當轉換為人類可讀的字串時,浮點數可能會產生許多小數位數。對於使用者輸出而言,這種細節通常會分散注意力,並且顯示一些小數位數就足夠了。
小數位數可以直接在 #format
方法中配置
123_456.789.format(decimal_places: 2) # => "123,456.79"
123_456.789.format(decimal_places: 0) # => "123,457"
123_456.789.format(decimal_places: 4) # => "123,456.7890"
與在格式化之前手動對值進行四捨五入相比,這更容易,並且允許更多選項。
預設情況下,小數位數是固定的。僅當 only_significant
為 true
時,才會省略尾隨的零。
123_456.789.format(decimal_places: 6) # => "123,456.789000"
123_456.789.format(decimal_places: 6, only_significant: true) # => "123,456.789"
人性化數字
當將不同數量級的數字放在一起時,很難以有意義的方式表示大範圍的值。
在這種情況下,通常使用量詞來表示值的大小。
為此,我們有 Number#humanize
:它將數字四捨五入到最接近的千位數量級,並帶有特定數量的有效數字。
1_200_000_000.humanize # => "1.2G"
0.000_000_012.humanize # => "12.0n"
它與 Number#format
具有相同的小數 separator
和千位 delimiter
參數,因此樣式可以完全相同的方式配置。
有效位數可以通過 precision
調整。但是預設值 3
可能已經非常適合大多數應用程式。當 siginficant
為 true
時,precision
的值是固定的十進位數字,與數字的值無關。
量詞預設為 SI 字首(k
、M
、G
等),但它們是完全可配置的,可以透過提供清單或 proc 來配置。
可自訂的量詞
Number#humanize
可以接受 proc 參數,該參數會計算特定數量級的位數和量詞。
以下範例顯示如何以公制單位格式化長度,包括單位指示符。它透過對介於 0.01
和 0.99
之間的值使用常見的公分單位來衍生自預設實作(通用對應將其表示為毫米)。所有其他值都使用通用 SI 字首(由 Number.si_prefix
提供)。
def humanize_length(number)
number.humanize do |magnitude, number|
case magnitude
when -2, -1 then {-2, " cm"}
else
magnitude = Number.prefix_index(magnitude)
{magnitude, " #{Number.si_prefix(magnitude)}m"}
end
end
end
humanize_length(1_420) # => "1.42 km"
humanize_length(0.23) # => "23.0 cm"
humanize_length(0.05) # => "5.0 cm"
humanize_length(0.001) # => "1.0 mm"
人性化位元組
第三個方法是 Int#humanize_bytes
,它允許以典型的格式格式化位元組數(例如記憶體大小)。它同時支援 IEC(Ki
、Mi
、Gi
、Ti
、Pi
、Ei
、Zi
、Yi
)和 JEDEC(K
、M
、G
、T
、P
、E
、Z
、Y
)字首。
1.humanize_bytes # => "1B"
1024.humanize_bytes # => "1.0kiB"
1536.humanize_bytes # => "1.5kiB"
524288.humanize_bytes(format: :JEDEC) # => "512kB"
1073741824.humanize_bytes(format: :JEDEC) # => "1.0GB"
此方法的 實作 是另一個基於 Numer#humanize
的自訂格式範例。
總結
這些新方法提供了很棒的功能,可以使數字對讀者而言看起來很漂亮。
它們不提供特定地區的樣式對應。這是一項非凡的任務,應留給專用的 I18N 程式庫。但是,它們是此類程式庫可以建立的有用構建模組。而且當您不需要支援不同的地區時,它們可以立即使用。
但是,實作並不完美。本地化很複雜,並且很難正確。一如既往,魔鬼藏在細節中。例如,千位分隔符和群組大小是可配置的,但具有固定值。無法以這種方式表示印度數字系統。然後僅支援阿拉伯數字。而且可能還有許多其他情況需要更專業的行為。
但是對於 90% 以上的典型用例來說,它可能是不錯的,並且在許多地方已經很有用。並且總有改進的空間。
可以在帶來這些功能的 PR中找到更多背景資訊。
從更一般的角度來看,關於格式化數字的另一篇好文章:Hjalmar Gislason 的為機器和凡人格式化數字。