Gofmt.Printf
は 3000 のコンマを含む数値の出力をサポートしていますか?
fmt.Printf("%d", 1000)
outputs 、代わり1000
に出力するためにどの形式を指定できますか?1,000
私はこのためのライブラリと、他のいくつかの人間表現に関する懸念事項を作成しました。
結果の例:
0 -> 0
100 -> 100
1000 -> 1,000
1000000000 -> 1,000,000,000
-100000 -> -100,000
使用例:
fmt.Printf("You owe $%s.\n", humanize.Comma(6582491))
fmt 印刷動詞は、3 桁ごとの区切り文字をサポートしていません。
序文:私はこのユーティリティを でさらにカスタマイズしてリリースしました。github.com/icza/gox
を参照してくださいfmtx.FormatInt()
。
パッケージは小数のfmt
グループ化をサポートしていません。
自分で実装する (または既存のものを使用する) 必要があります。
コンパクトで非常に効率的なソリューションを次に示します (後の説明を参照)。
Go Playgroundで試してみてください。
func Format(n int64) string {
in := strconv.FormatInt(n, 10)
numOfDigits := len(in)
if n < 0 {
numOfDigits-- // First character is the - sign (not a digit)
}
numOfCommas := (numOfDigits - 1) / 3
out := make([]byte, len(in)+numOfCommas)
if n < 0 {
in, out[0] = in[1:], '-'
}
for i, j, k := len(in)-1, len(out)-1, 0; ; i, j = i-1, j-1 {
out[j] = in[i]
if i == 0 {
return string(out)
}
if k++; k == 3 {
j, k = j-1, 0
out[j] = ','
}
}
}
テスト:
for _, v := range []int64{0, 1, 12, 123, 1234, 123456789} {
fmt.Printf("%10d = %12s\n", v, Format(v))
fmt.Printf("%10d = %12s\n", -v, Format(-v))
}
出力:
0 = 0
0 = 0
1 = 1
-1 = -1
12 = 12
-12 = -12
123 = 123
-123 = -123
1234 = 1,234
-1234 = -1,234
123456789 = 123,456,789
-123456789 = -123,456,789
基本的に、関数が行うことは、グループ化せずに数値をフォーマットし、十分な大きさの他のスライスを作成し、必要に応じて (数字がさらにある場合は 3 の数字のグループの後) 必要に応じてグループ化記号Format()
を挿入して数字の数字をコピーすることです。','
保持される負の符号に注意してください。
出力の長さ:
基本的には、入力の長さに挿入するグループ化記号の数を加えた長さです。グループ化記号の数は次のとおりです。
numOfCommas = (numOfDigits - 1) / 3
入力文字列は数字 ( '0..9'
) とオプションでマイナス記号 ( '-'
) のみを含む数値であるため、文字は単純に UTF-8 エンコーディングで 1 対 1 の方法でバイトにマップされます (これは Go が文字列をメモリー)。そのため、ルーンの代わりにバイトを簡単に操作できます。したがって、桁数は入力文字列の長さであり1
、数値が負の場合はオプションでマイナスになります。
numOfDigits := len(in)
if n < 0 {
numOfDigits-- // First character is the - sign (not a digit)
}
したがって、グループ化記号の数:
numOfCommas := (numOfDigits - 1) / 3
したがって、出力スライスは次のようになります。
out := make([]byte, len(in)+numOfCommas)
負符号文字の処理:
数値が負の場合、入力文字列をスライスして処理から除外し、手動で符号ビットを出力にコピーします。
if n < 0 {
in, out[0] = in[1:], '-'
}
したがって、関数の残りの部分は、オプションのマイナス記号文字を知る/気にする必要はありません。
関数の残りの部分は、for
数字のバイト (数字) を入力文字列から出力にコピーするループであり、','
さらに数字がある場合は、3 桁の各グループの後にグループ化記号 ( ) を挿入します。ループは下に行くので、3 桁のグループを追跡しやすくなります。完了すると (数字がなくなると)、出力バイト スライスが として返されますstring
。
効率よりも読みやすさに関心がある場合は、次のバージョンが気に入るかもしれません。
func Format2(n int64) string {
if n < 0 {
return "-" + Format2(-n)
}
in := strconv.FormatInt(n, 10)
numOfCommas := (len(in) - 1) / 3
out := make([]byte, len(in)+numOfCommas)
for i, j, k := len(in)-1, len(out)-1, 0; ; i, j = i-1, j-1 {
out[j] = in[i]
if i == 0 {
return string(out)
}
if k++; k == 3 {
j, k = j-1, 0
out[j] = ','
}
}
}
基本的に、これは再帰呼び出しで負の数を処理します。数値が負の場合、絶対 (正) 値でそれ自体を (再帰的に) 呼び出し、結果の先頭に"-"
文字列を追加します。
append()
付き組み込みappend()
関数とスライス操作を使用した別のバージョンを次に示します。やや理解しやすいですが、パフォーマンスに関してはそれほど良くありません:
func Format3(n int64) string {
if n < 0 {
return "-" + Format3(-n)
}
in := []byte(strconv.FormatInt(n, 10))
var out []byte
if i := len(in) % 3; i != 0 {
if out, in = append(out, in[:i]...), in[i:]; len(in) > 0 {
out = append(out, ',')
}
}
for len(in) > 0 {
if out, in = append(out, in[:3]...), in[3:]; len(in) > 0 {
out = append(out, ',')
}
}
return string(out)
}
最初のif
ステートメントは、存在する場合は 3 桁未満の最初のオプションの「不完全な」グループを処理し、後続のfor
ループは残りを処理し、各反復で 3 桁をコピーし、','
さらに桁がある場合はコンマ ( ) グループ記号を追加します。 .
ユーザー指定の桁区切り記号、小数点記号、および小数点以下の精度に従って数値 (float64 または int) をレンダリングする関数の Go スニペットを Github に公開しました。
https://gist.github.com/gorhill/5285193
使用法: s := RenderFloat(format, n) format パラメーターは、数値 n をレンダリングする方法を示します。 n = 12345.6789 の場合のフォーマット文字列の例: "#,###.##" => "12,345.67" 「#、###。」=>「12,345」 "#,###" => "12345,678" "#\u202F###,##" => "12 345,67" "#.###,###### => 12.345,678900 "" (別名デフォルト形式) => 12,345.67
以前の回答で提供されたソリューションのパフォーマンスに興味を持ち、それらのベンチマークを使用してテストを作成しました。これには、私の 2 つのコード スニペットが含まれます。次の結果は、MacBook 2018、i7 2.6GHz で測定されました。
+---------------------+-------------------------------------------+--------------+
| Author | Description | Result |
|---------------------|-------------------------------------------|--------------|
| myself | dividing by 1,000 and appending groups | 3,472 ns/op |
| myself | inserting commas to digit groups | 2,662 ns/op |
| @icza | collecting digit by digit to output array | 1,695 ns/op |
| @dolmen | copying digit groups to output array | 1,797 ns/op |
| @Ivan Tung | writing digit by digit to buffer | 2,753 ns/op |
| @jchavannes | inserting commas using a regexp | 63,995 ns/op |
| @Steffi Keran Rani, | using github.com/dustin/go-humanize | 3,525 ns/op |
| @abourget, @Dustin | | |
| @dolmen | using golang.org/x/text/message | 12,511 ns/op |
+---------------------+-------------------------------------------+--------------+
他の手作業でコーディングされたソリューションも高速であり、regexp の使用を除いて、どれを選択しても後悔することはありません。regexp を使用するには最短のコード スニペットが必要ですが、パフォーマンスが非常に悪く、その価値はありません。
このトピックへの私の貢献は、プレイグラウンドで実行してみることができます:
func formatInt(number int) string {
output := strconv.Itoa(number)
startOffset := 3
if number < 0 {
startOffset++
}
for outputIndex := len(output); outputIndex > startOffset; {
outputIndex -= 3
output = output[:outputIndex] + "," + output[outputIndex:]
}
return output
}
https://github.com/dustin/go-humanizeを使用してください..それらを処理するためのヘルパーがたくさんあります。MiB、MB、およびその他のグッズとしてのバイトに加えて。
ライブラリを使用したくない場合 (何らかの理由で)、私はこれをノックアップしました。動作しているようで、指定されたルーンを区切り文字として使用できます。
import (
"strconv"
)
func delimitNumeral(i int, delim rune) string {
src := strconv.Itoa(i)
strLen := utf8.RuneCountInString(src)
outStr := ""
digitCount := 0
for i := strLen - 1; i >= 0; i-- {
outStr = src[i:i+1] + outStr
if digitCount == 2 {
outStr = string(delim) + outStr
digitCount = 0
} else {
digitCount++
}
}
return outStr
}
注:さらにテストした結果、この機能は完全には機能しません。@IvanTung によって投稿されたソリューションを使用することをお勧めします。また、私のものを完全に機能させることができる人からの編集を歓迎します。
import ("fmt"; "strings")
func commas(s string) string {
if len(s) <= 3 {
return s
} else {
return commas(s[0:len(s)-3]) + "," + s[len(s)-3:]
}
}
func toString(f float64) string {
parts := strings.Split(fmt.Sprintf("%.2f", f), ".")
if parts[0][0] == '-' {
return "-" + commas(parts[0][1:]) + "." + parts[1]
}
return commas(parts[0]) + "." + parts[1]
}