Go 言語(以下 Golang)で、数字を3桁ごとにカンマ区切りする処理をしたい。
つまり 1000 の単位で区切りたいのです。例えば
int(1000)
--->"1,000"
とコンマ区切りでフォーマット出力したい。しかし、fmt
パッケージには数字のフォーマット処理をする専用の関数がなさげ。どうしよう。
独自実装やサードパーティのパッケージを使わずに、golang の純正パッケージでフォーマットできないものか、「golang コンマ区切り カンマ区切り 出力 フォーマット」でググるも独自実装だったり、すでにコンマ区切りの値を数値に変換する処理といったものばかりだったので自分のググラビリティとして。
TL; DR (今北産業)
-
Go の準標準モジュール
golang.org/x/text
の message パッケージで可能
・message
は、ロケールに合わせたテキスト整形ツール郡でfmt
の代替パッケージ -
桁の区切りカンマやピリオドは国によって記法が異なる。
message
で調整が可能 -
マスター、動くものをくれ
基本構文package main import ( "golang.org/x/text/language" "golang.org/x/text/message" ) func main() { value := 1000 fmtX := message.NewPrinter(language.Japanese) fmtX.Printf("%d\n", value) // Output: 1,000 }
- オンラインで動作をみる @ Go playground
言語による表記の違いpackage main import ( "golang.org/x/text/language" "golang.org/x/text/message" "golang.org/x/text/number" ) func main() { value := 1234567.89 for _, langTag := range []language.Tag{ language.English, language.Japanese, language.French, language.Hindi, language.German, } { p := message.NewPrinter(langTag) p.Printf("%s: %v\n", langTag, number.Decimal(value)) } // Output: // en: 1,234,567.89 // ja: 1,234,567.89 // fr: 1 234 567,89 // hi: 12,34,567.89 // de: 1.234.567,89 }
- オンラインで動作をみる @ Go playground
- 併せて読みたい
- 特定言語のフォーマッターを利用する例
- 【Golang】GoからPythonを呼び出してみよう【Python】 @ Qiita
TS; DR
1000 単位の区切りにコンマは国によって異なる
小数点や桁の区切りは、国によって記法が異なることをご存じでしょうか。
例えば、日本では 1234567.89
という値は 1,234,567.89
とも「百二十三万四千五百六十七点八九」とも記載できます。
しかし、1.234.567,89
だったり 12,34,567.89
、1 234 567,89
や 1'234'567,89
という、日本とは異なる記法の国も多いのです。
他にも、日付の 2001 年 10 月 11 日を 01/10/11
という YMD
記法の国もあれは、11/10/01
(DMY
)だったり 10/11/01
(MDY
)という国もあります。
つまり、国によって記法が異なるということです。
表記のローカライズ
そこで、「その国に合わせた記述方法」に変換することを「ローカライズ」(地域化)と言います。
OS で「ロケール」を変更すると、数値や日付の表記が変わるのを経験したことはないでしょうか。アレです。
ゆうて、筆者も「PHP なら number_format
、Python なら f 文字列の f"{num:,}"
があるのに、なぜ Go には標準で数値のフォーマッターがないんだ」と思い調べていたところ、number モジュールがフォーマッターであることはわかったものの、message
の Printer
オブジェクトを通して処理しています。
なぜそんな七面倒臭いことをするのだろうと思って message のドキュメントを読んでいたら「ロケールの問題か!」と気付かされたのです。
message から以下の抜粋を翻訳してみました。
Localized Formatting
A format string can be localized by replacing any of the print functions of fmt with an equivalent call to a Printer.
p := message.NewPrinter(message.MatchLanguage("en")) p.Println(123456.78) // Prints 123,456.78 p.Printf("%d ducks in a row", 4331) // Prints 4,331 ducks in a row p := message.NewPrinter(message.MatchLanguage("nl")) p.Printf("Hoogte: %.1f meter", 1244.9) // Prints Hoogte: 1,244.9 meter p := message.NewPrinter(message.MatchLanguage("bn")) p.Println(123456.78) // Prints ১,২৩,৪৫৬.৭৮
Printer currently supports numbers and specialized types for which packages exist in x/text. Other builtin types such as time.Time and slices are planned.
Format strings largely have the same meaning as with fmt with the following notable exceptions:
- flag # always resorts to fmt for printing
- verb 'f', 'e', 'g', 'd' use localized formatting unless the '#' flag is specified.
- verb 'm' inserts a translation of a string argument.
See package fmt for more options.
(Localized Formatting | message @ pkg.go.dev より)
▼ 以下筆者翻訳
ローカライズされたフォーマットによる文字列整形
文字列のフォーマット(整形)は、
fmt
のmessage.Printer
オブジェクトのメソッドに置き換えることでローカライズ(地域化)することができます。p := message.NewPrinter(message.MatchLanguage("en")) p.Println(123456.78) // 出力: "123,456.78" p.Printf("%d ducks in a row", 4331) // 出力: "4,331 ducks in a row" p := message.NewPrinter(message.MatchLanguage("nl")) p.Printf("Hoogte: %.1f meter", 1244.9) // 出力: "Hoogte: 1,244.9 meter" p := message.NewPrinter(message.MatchLanguage("bn")) p.Println(123456.78) // 出力: "১,২৩,৪৫৬.৭৮"
現在、
message.Printer
型は、数字とx/text
パッケージに存在する特殊な型のみをサポートしています。その他のtime.Time
やスライスなどのビルトインの型は、今後実装が予定されています。文字列のフォーマットは、以下の顕著な例外を除いて、
fmt
とほぼ同じ意味を持っています
#
フラグは常にfmt
を使用します- "
%d
" などのf
,e
,g
,d
動詞は、#
フラグが指定されない限り、地域化された書式を使用しますm
動詞は、引数の文字列の翻訳を挿入します。(※)その他のオプションについては
fmt
パッケージのドキュメントをご覧ください。
※ 【筆者注】 message
には国際化のための翻訳辞書機能があるようです。しかし、ドキュメントのサンプルが動かないため、使い方もよくわかりません。分かり次第追記か別記事にしたいと思います。
参考文献
- Localized Formatting | message | text | golang.org @ pkg.go.dev
- "How to fmt.Printf an integer with thousands comma" @ Stackoverflow