string 如何 fmt.Printf 带有千位逗号的整数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13020308/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to fmt.Printf an integer with thousands comma
提问by BrandonAGr
Does Go's fmt.Printf
support outputting a number with the thousands comma?
Go 是否fmt.Printf
支持输出带有千位逗号的数字?
fmt.Printf("%d", 1000)
outputs 1000
, what format can I specify to output 1,000
instead?
fmt.Printf("%d", 1000)
输出1000
,我可以指定什么格式来输出1,000
?
The docsdon't seem to mention commas, and I couldn't immediately see anything in the source.
采纳答案by zzzz
None of the fmt print verbs support thousands separators.
没有一个 fmt 打印动词支持千位分隔符。
回答by dolmen
Use golang.org/x/text/message
to print using localized formatting for any language in the Unicode CLDR:
用于golang.org/x/text/message
使用Unicode CLDR 中任何语言的本地化格式打印:
package main
import (
"golang.org/x/text/language"
"golang.org/x/text/message"
)
func main() {
p := message.NewPrinter(language.English)
p.Printf("%d\n", 1000)
// Output:
// 1,000
}
回答by Dustin
I wrote a library for thisas well as a few other human-representation concerns.
我为此编写了一个库以及其他一些人类表示问题。
Example results:
结果示例:
0 -> 0
100 -> 100
1000 -> 1,000
1000000000 -> 1,000,000,000
-100000 -> -100,000
Example Usage:
示例用法:
fmt.Printf("You owe $%s.\n", humanize.Comma(6582491))
回答by R. Hill
I published a Go snippet over at Github of a function to render a number (float64 or int) according to user-specified thousand separator, decimal separator and decimal precision.
我在 Github 上发布了一个 Go 代码片段,该函数根据用户指定的千位分隔符、小数分隔符和小数精度呈现数字(float64 或 int)。
https://gist.github.com/gorhill/5285193
https://gist.github.com/gorhill/5285193
Usage: s := RenderFloat(format, n) The format parameter tells how to render the number n. Examples of format strings, given n = 12345.6789: "#,###.##" => "12,345.67" "#,###." => "12,345" "#,###" => "12345,678" "#\u202F###,##" => "12?345,67" "#.###,###### => 12.345,678900 "" (aka default format) => 12,345.67
回答by icza
Foreword:I released this utility with more customization in github.com/icza/gox
, see fmtx.FormatInt()
.
前言:我在发布此工具时带有更多的定制github.com/icza/gox
,请参阅fmtx.FormatInt()
。
The fmt
package does not support grouping decimals.
该fmt
包不支持分组小数。
We have to implement one ourselves (or use an existing one).
我们必须自己实现一个(或使用现有的)。
The Code
编码
Here is a compact and really efficient solution (see explanation after):
这是一个紧凑且真正有效的解决方案(见后面的解释):
Try it on the Go Playground.
在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] = ','
}
}
}
Testing it:
测试它:
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))
}
Output:
输出:
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
Explanation:
解释:
Basically what the Format()
function does is it formats the number without grouping, then creates a big enough other slice and copies the digits of the number inserting comma (','
) grouping symbol when necessary (after groups of digits of 3 if there are more digits) meanwhile taking care of the negative sign to be preserved.
基本上该Format()
函数的作用是在不分组的情况下格式化数字,然后创建一个足够大的其他切片并','
在必要时复制数字的数字插入逗号()分组符号(如果有更多数字,则在 3 位数字组之后)同时取注意要保留的负号。
The length of the output:
输出长度:
It is basically the length of the input plus the number of grouping signs to be inserted. The number of grouping signs is:
它基本上是输入的长度加上要插入的分组符号的数量。分组标志的数量为:
numOfCommas = (numOfDigits - 1) / 3
Since the input string is a number which may only contain digits ('0..9'
) and optionally a negative sign ('-'
), the characters are simply mapped to bytes in a 1-to-1 fashion in UTF-8 encoding (this is how Go stores strings in memory). So we can simply work with bytes instead of runes. So the number of digits is the input string length, optionally minus 1
if the number is negative:
由于输入字符串是一个可能只包含数字 ( '0..9'
) 和可选的负号 ( '-'
) 的数字,因此字符以 UTF-8 编码的 1 对 1 方式简单地映射到字节(这就是 Go 将字符串存储在记忆)。所以我们可以简单地使用字节而不是符文。所以位数是输入字符串的长度,1
如果数字是负数,可以选择减去:
numOfDigits := len(in)
if n < 0 {
numOfDigits-- // First character is the - sign (not a digit)
}
And therefore the number of grouping signs:
因此分组标志的数量:
numOfCommas := (numOfDigits - 1) / 3
Therefore the output slice will be:
因此输出切片将是:
out := make([]byte, len(in)+numOfCommas)
Handling the negative sign character:
处理负号字符:
If the number is negative, we simply slice the input string to exclude it from processing and we manually copy the sign bit to the output:
如果数字是负数,我们只需将输入字符串切片以将其排除在处理之外,然后我们手动将符号位复制到输出:
if n < 0 {
in, out[0] = in[1:], '-'
}
And therefore the rest of the function does not need to know/care about the optional negative sign character.
因此函数的其余部分不需要知道/关心可选的负号字符。
The rest of the function is a for
loop which just copies the bytes (digits) of the number from the input string to the output, inserting a grouping sign (','
) after every group of 3 digits if there are more digits. The loop goes downward so it's easier to track the groups of 3 digits. Once done (no more digits), the output byte slice is returned as a string
.
该函数的其余部分是一个for
循环,它只是将数字的字节(数字)从输入字符串复制到输出,','
如果有更多数字,则在每组 3 位数字后插入一个分组符号 ( )。循环向下,因此更容易跟踪 3 位数字组。完成后(不再有数字),输出字节切片将作为string
.
Variations
变化
Handling negative with recursion
用递归处理负数
If you're less concerned with efficiency and more about readability, you might like this version:
如果你不太关心效率而更关心可读性,你可能会喜欢这个版本:
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] = ','
}
}
}
Basically this handles negative numbers with a recursive call: if the number is negative, calls itself (recursive) with the absolute (positive) value and prepends the result with a "-"
string.
基本上这通过递归调用处理负数:如果数字是负数,则使用绝对(正)值调用自身(递归)并在结果前加上一个"-"
字符串。
With append()
slices
带append()
切片
Here's another version using the builtin append()
function and slice operations. Somewhat easier to understand but not so good performance-wise:
这是使用内置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)
}
The first if
statement takes care of the first optional, "incomplete" group which is less than 3 digits if exists, and the subsequent for
loop handles the rest, copying 3 digits in each iteration and appending a comma (','
) grouping sign if there are more digits.
第一条if
语句处理第一个可选的“不完整”组,如果存在,该组少于 3 位,随后的for
循环处理其余部分,在每次迭代中复制 3 位,','
如果有更多位,则附加一个逗号 ( ) 分组符号.
回答by Ivan Tung
Here is a function that takes an integer and grouping separator and returns a string delimited with the specified separator. I have tried to optimize for efficiency, no string concatenation or mod/division in the tight loop. From my profiling it is more than twice as fast as the humanize.Commas implementation (~680ns vs 1642ns) on my Mac. I am new to Go, would love to see faster implementations!
这是一个函数,它接受一个整数和分组分隔符,并返回一个用指定分隔符分隔的字符串。我试图优化效率,在紧密循环中没有字符串连接或 mod/division。从我的分析来看,它比我的 Mac 上的 humanize.Commas 实现(~680ns vs 1642ns)快两倍多。我是 Go 的新手,希望看到更快的实现!
Usage: s := NumberToString(n int, sep rune)
用法:s := NumberToString(n int, sep rune)
Examples
例子
Illustrates using different separator (',' vs ' '), verified with int value range.
说明使用不同的分隔符 (',' vs ' '),用 int 值范围验证。
s:= NumberToString(12345678, ',')
s:= NumberToString(12345678, ',')
=> "12,345,678"
=> "12,345,678"
s:= NumberToString(12345678, ' ')
s:= NumberToString(12345678, ' ')
=> "12 345 678"
=> "12 345 678"
s: = NumberToString(-9223372036854775807, ',')
s: = NumberToString(-9223372036854775807, ',')
=> "-9,223,372,036,854,775,807"
=> "-9,223,372,036,854,775,807"
Function Implementation
功能实现
func NumberToString(n int, sep rune) string {
s := strconv.Itoa(n)
startOffset := 0
var buff bytes.Buffer
if n < 0 {
startOffset = 1
buff.WriteByte('-')
}
l := len(s)
commaIndex := 3 - ((l - startOffset) % 3)
if (commaIndex == 3) {
commaIndex = 0
}
for i := startOffset; i < l; i++ {
if (commaIndex == 3) {
buff.WriteRune(sep)
commaIndex = 0
}
commaIndex++
buff.WriteByte(s[i])
}
return buff.String()
}
回答by jchavannes
Here's a simple function using regex:
这是一个使用正则表达式的简单函数:
import (
"regexp"
)
func formatCommas(num int) string {
str := fmt.Sprintf("%d", num)
re := regexp.MustCompile("(\d+)(\d{3})")
for n := ""; n != str; {
n = str
str = re.ReplaceAllString(str, ",")
}
return str
}
Example:
例子:
fmt.Println(formatCommas(1000))
fmt.Println(formatCommas(-1000000000))
Output:
输出:
1,000
-1,000,000,000
回答by abourget
Use https://github.com/dustin/go-humanize.. it has a bunch of helpers to deal with those things. In addition to bytes as MiB, MB, and other goodies.
使用https://github.com/dustin/go-humanize.. 它有一堆帮手来处理这些事情。除了字节作为 MiB、MB 和其他好东西。
回答by Steffi Keran Rani J
The package humanizecan do the magic! Refer the documentation of this package here. To use this package, install it first by using a tool like Git SCM. If you are using Git Bash, open the shell window and type:
人性化的包装可以发挥作用!请在此处参阅此包的文档。要使用此包,请先使用 Git SCM 之类的工具安装它。如果您使用的是 Git Bash,请打开 shell 窗口并键入:
go get -u github.com/dustin/go-humanize
Once this is done, you can use the following solution code (Say, main.go):
完成此操作后,您可以使用以下解决方案代码(例如,main.go):
package main
import (
"fmt"
"github.com/dustin/go-humanize"
)
func main() {
fmt.Println(humanize.Commaf(float64(123456789)));
fmt.Println(humanize.Commaf(float64(-1000000000)));
fmt.Println(humanize.Commaf(float64(-100000.005)));
fmt.Println(humanize.Commaf(float64(100000.000)));
}
There are other variations to Commaf
like BigComma, Comma, BigCommaf
etc. which depends on the data type of your input.
还有其他Commaf
喜欢的变体BigComma, Comma, BigCommaf
等等,这取决于您输入的数据类型。
So, on running this program using the command:
因此,在使用以下命令运行此程序时:
go run main.go
You will see an outputsuch as this:
您将看到如下输出:
123,456,789
-1,000,000,000
-100,000.005
100,000
回答by Ferdinand Prantl
I got interested in the performance of solutions offered in earlier answers and wrote tests with benchmarks for them, including two code snippets of mine. The following results were measured on MacBook 2018, i7 2.6GHz:
我对早期答案中提供的解决方案的性能感兴趣,并为它们编写了带有基准测试的测试,包括我的两个代码片段。以下结果是在 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 |
+---------------------+-------------------------------------------+--------------+
- If you want the fastest solution, grab @icza's code snippet. Although it goes digit by digit and not by groups of three digits, it emerged as the fastest.
- If you want the shortest reasonable code snippet, look at mine below. It adds more than half of the time of the fastest solution, but the code is three times shorter.
- If you want a one-liner and do not mind using an external library, go for github.com/dustin/go-humanize. It is more than twice slower as the fastest solution, but the library might help you with other formatting.
- If you want localized output, choose golang.org/x/text/message. It is seven times slower than the fastest solution, but the luxury of matching the consumer's language does not come free.
- 如果您想要最快的解决方案,请获取@icza 的代码片段。虽然它是一个数字一个数字,而不是三个数字一组,但它是最快的。
- 如果你想要最短的合理代码片段,请看下面的我的。它增加了最快解决方案一半以上的时间,但代码缩短了三倍。
- 如果你想要一个单线并且不介意使用外部库,请访问github.com/dustin/go-humanize。它比最快的解决方案慢两倍多,但该库可能会帮助您处理其他格式。
- 如果您想要本地化输出,请选择golang.org/x/text/message。它比最快的解决方案慢七倍,但匹配消费者语言的奢侈并不是免费的。
Other hand-coded solutions are fast too and you will not regret choosing any of them, except for the usage of regexp. Using regexp needs the shortest code snippet, but the performance is so tragic, that it is not worth it.
其他手工编码的解决方案也很快,您不会后悔选择其中任何一个,除了使用正则表达式。使用regexp需要最短的代码片段,但是性能太悲剧了,不值得。
My contribution to this topic, which you can try running in the playground:
我对这个主题的贡献,你可以尝试在操场上运行:
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
}