Go的fmt.Printf
是否支持输出带有数千个逗号的数字?
fmt.Printf("%d", 1000)
输出1000
,我可以指定哪种格式来输出1,000
?
答案 0 :(得分:45)
我写了a library for this以及其他一些人为代表的问题。
示例结果:
0 -> 0
100 -> 100
1000 -> 1,000
1000000000 -> 1,000,000,000
-100000 -> -100,000
示例用法:
fmt.Printf("You owe $%s.\n", humanize.Comma(6582491))
答案 1 :(得分:38)
使用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
}
答案 2 :(得分:23)
fmt打印动词都不支持千位分隔符。
答案 3 :(得分:11)
我在Github上发布了一个Go片段,用于根据用户指定的千位分隔符,小数分隔符和小数精度呈现数字(float64或int)。
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
答案 4 :(得分:10)
fmt
包不支持对小数进行分组。
我们必须自己实施(或使用现有的)。
这是一个紧凑而真正有效的解决方案(见后面的解释):
在Go Playground上尝试。
func Format(n int64) string {
in := strconv.FormatInt(n, 10)
out := make([]byte, len(in)+(len(in)-2+int(in[0]/'0'))/3)
if in[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
基本上Format()
函数的作用是在没有分组的情况下格式化数字,然后创建足够大的其他切片并在必要时复制插入逗号(','
)分组符号的数字的数字(之后)如果有更多数字,则为3的数字组)同时处理要保留的负号。
输出的长度:
基本上是输入的长度加上要插入的分组符号的数量。分组标志的数量是:
numOfCommas = (numOfDigits - 1) / 3
由于输入字符串是一个只能包含数字('0..9'
)且可选地包含负号('-'
)的数字,因此字符只是以1对1的方式映射到字节用UTF-8编码(这就是Go在内存中存储字符串的方式)。所以我们可以简单地使用字节而不是符文。因此,位数是输入字符串长度,对于符号数字1
,可选择减去'-'
。
如果有符号数字,它将在in[0]
中。 '-'
的数值为45
,而数字字符'0'..'9'
的数值为48..57
。因此符号字符小于可能的数字。因此,如果我们按'0'
划分第一个字符(总是至少有一个字符),如果是负号,我们会得到0
,如果是一个数字,我们得到1
(整数除法) )。
因此输入字符串中的位数为:
numOfDigits = len(in) - 1 + int(in[0]/'0')
因此分组标志的数量:
numOfCommas = (len(in) - 2 + int(in[0]/'0')) / 3
因此输出切片将是:
out := make([]byte, len(in)+(len(in)-2+int(in[0]/'0'))/3)
处理负号字符
如果数字为负数,我们只需将输入字符串切片以将其排除在处理之外,我们手动将符号位复制到输出中:
if in[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)
out := make([]byte, len(in)+(len(in)-1)/3)
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
语句负责第一个可选项,&#34;不完整&#34;如果存在,则小于3位的组,后续的for
循环处理其余的,在每次迭代中复制3位数,如果有更多位数,则附加逗号(','
)分组符号。
答案 5 :(得分:6)
这是一个函数,它接受一个整数和分组分隔符,并返回一个用指定分隔符分隔的字符串。我试图优化效率,在紧密循环中没有字符串连接或mod / division。从我的分析来看,它比我的Mac上的人体化速度快了两倍。在我的Mac上实现(~680ns对比1642ns)。我是Go的新手,很想看到更快的实现!
用法:s:= NumberToString(n int,sep rune)
示例
说明使用不同的分隔符(&#39;,&#39; vs&#39;&#39;),使用int值范围进行验证。
s:= NumberToString(12345678,&#39;,&#39;)
=&GT; &#34; 12345678&#34;
s:= NumberToString(12345678,&#39;&#39;)
=&GT; &#34; 12 345 678&#34;
s:= NumberToString(-9223372036854775807,&#39;,&#39;)
=&GT; &#34; -9,223,372,036,854,775,807&#34;
功能实施
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()
}
答案 6 :(得分:2)
这是一个使用正则表达式的简单函数:
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, "$1,$2")
}
return str
}
示例:
fmt.Println(formatCommas(1000))
fmt.Println(formatCommas(-1000000000))
输出:
1,000
-1,000,000,000
答案 7 :(得分:1)
使用https://github.com/dustin/go-humanize ..它有一堆助手来处理这些事情。除了作为MiB,MB和其他好东西的字节。
答案 8 :(得分:0)
我对早期答案中提供的解决方案的性能很感兴趣,并写了tests with benchmarks for them,包括我的两个代码片段。在MacBook 2018 i7 2.6GHz上测量了以下结果:
> variance_homo(iris,'Species','Sepal.Length')
Error in bartlett.test.default("Sepal.Length", "Species") :
all observations are in the same group
Called from: bartlett.test.default("Sepal.Length", "Species")
其他手动编码的解决方案也很快,除了使用regexp之外,您不会后悔选择其中任何一个。使用regexp需要最短的代码段,但是性能是如此可悲,以至于不值得。
我对此主题的贡献,您可以try running in the playground:
+---------------------+-------------------------------------------+--------------+
| 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 |
+---------------------+-------------------------------------------+--------------+
答案 9 :(得分:0)
您还可以使用以下小型包装:https://github.com/floscodes/golang-thousands。
只需将您的数字转换为字符串,然后使用Separate
函数,如下所示:
n:="3478686" // your number as a string
thousands.Separate(n, "en") // adds thousands separators. the second argument sets the language mode.
答案 10 :(得分:0)
这绝对不是基准测试的领导者,但是谁会在乎代码是否清晰并且性能是否至关重要?
package main
import (
"fmt"
)
func IntComma(i int) string {
if (i < 0) {
return "-" + IntComma(-i)
}
if (i < 1000) {
return fmt.Sprintf("%d",i)
}
return IntComma(i / 1000) + "," + fmt.Sprintf("%03d",i % 1000)
}
func main() {
fmt.Println(IntComma(1234567891234567))
}
这是针对基准测试的:实现与icza非常相似
func IntCommaB(num int) string {
str := strconv.Itoa(num)
l_str := len(str)
digits := l_str
if num < 0 {
digits--
}
commas := (digits + 2) / 3 - 1
l_buf := l_str + commas
var sbuf [32]byte // pre allocate buffer at stack rather than make([]byte,n)
buf := sbuf[0:l_buf]
// copy str from the end
for s_i, b_i, c3 := l_str-1, l_buf-1, 0; ; {
buf[b_i] = str[s_i]
if s_i == 0 {
return string(buf)
}
s_i--
b_i--
// insert comma every 3 chars
c3++
if c3 == 3 && (s_i > 0 || num>0) {
buf[b_i] = ','
b_i--
c3 = 0
}
}
}
输入-1234567890123456789,它比icza的速度快15%
答案 11 :(得分:-1)
如果你不想使用图书馆(无论出于何种原因),我就把它搞砸了。它似乎工作,可以使用任何指定的符文作为分隔符:
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发布的解决方案,并欢迎所有能让我的工作完美的人的任何编辑。
答案 12 :(得分:-1)
人性化包可以神奇!请参阅此软件包here的文档。要使用此软件包,请先使用Git SCM之类的工具进行安装。如果您使用的是Git Bash,请打开Shell窗口并输入:
go get -u github.com/dustin/go-humanize
完成此操作后,您可以使用以下解决方案代码(例如, 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)));
}
Commaf
还有其他变体,例如BigComma, Comma, BigCommaf
等,这取决于您输入的数据类型。
因此,使用以下命令运行该程序:
go run main.go
您将看到这样的输出:
123,456,789
-1,000,000,000
-100,000.005
100,000
答案 13 :(得分:-2)
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]
}