字符串-如何fmt.Printf具有数千个逗号的整数

Go的fmt.Printf是否支持输出带有数千个逗号的数字?

fmt.Printf("%d", 1000)输出1000,我可以指定什么格式来输出1,000

这些文档似乎没有提到逗号,而且我无法立即在源代码中看到任何内容。

BrandonAGr asked 2020-01-10T22:07:18Z
11个解决方案
50 votes

使用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
}
dolmen answered 2020-01-10T22:07:47Z
46 votes

我为此编写了一个库,以及其他一些有关人类表示的问题。

结果示例:

0 -> 0
100 -> 100
1000 -> 1,000
1000000000 -> 1,000,000,000
-100000 -> -100,000

用法示例:

fmt.Printf("You owe $%s.\n", humanize.Comma(6582491))
Dustin answered 2020-01-10T22:08:15Z
24 votes

fmt打印动词均不支持数千个分隔符。

zzzz answered 2020-01-10T22:07:27Z
12 votes

我在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
R. Hill answered 2020-01-10T22:08:40Z
11 votes

if程序包不支持分组小数。

我们必须自己实现(或使用现有的)。

代码

这是一个紧凑且非常有效的解决方案(请参阅后面的说明):

在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

说明:

基本上,if函数的作用是对数字进行格式化而不进行分组,然后创建足够大的其他切片,并在必要时复制数字插入逗号(for)分组符号的数字(如果有更多数字,请在3位数之后) 同时注意要保留的负号。

输出的长度:

它基本上是输入的长度加上要插入的分组符号的数量。 分组标志的数量为:

numOfCommas = (numOfDigits - 1) / 3

由于输入字符串是一个只能包含数字(if)和可选的负号(for)的数字,因此这些字符仅以UTF-8编码以一对一的方式映射到字节(这就是Go存储的方式) 内存中的字符串)。 因此,我们可以简单地使用字节而不是符文。 因此,位数是输入字符串的长度,对于符号位数'0'..'9',可以选择减去','

如果有符号数字,则为iffor的数字值为',',而数字字符'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:], '-'
}

因此,该功能的其余部分无需了解/关心可选的负号字符。

函数的其余部分是if循环,该循环仅将数字的字节(数字)从输入字符串复制到输出,如果还有更多数字,则在每3个数字组之后插入一个分组符号(for)。 循环向下,因此更容易跟踪3位数字的组。 完成操作(没有更多数字)后,输出字节片将作为','返回。

变化

递归处理否定

如果您不太关心效率,而更多地关注可读性,则可以选择以下版本:

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] = ','
        }
    }
}

基本上,这通过递归调用来处理负数:如果数字为负,则使用绝对(正)值调用自身(递归),并在结果前添加if字符串。

带有if切片

这是另一个使用内置if函数和切片操作的版本。 在某种程度上更容易理解,但在性能方面却不是那么好:

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位数字,如果存在则添加逗号(',')分组符号 是更多数字。

icza answered 2020-01-10T22:10:59Z
8 votes

这是一个采用整数和分组分隔符并返回以指定分隔符分隔的字符串的函数。 我试图优化效率,在紧密循环中没有字符串连接或mod / division。 从我的分析来看,它是Mac上的humanize.Commas实现的两倍(〜680ns与1642ns)。 我是Go的新手,很乐意看到更快的实现!

用法:s:= NumberToString(n int,sep rune)

例子

说明使用不同的分隔符(','vs''),并用int值范围验证。

s:= NumberToString(12345678,',')

=>“ 12,345,678”

s:= NumberToString(12345678,'')

=>“ 12 345 678”

s:= NumberToString(-9223372036854775807,',')

=>“ -9,223,372,036,854,775,807”

功能实现

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()
}
Ivan Tung answered 2020-01-10T22:12:03Z
2 votes

这是使用正则表达式的简单函数:

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

[https://play.golang.org/p/vnsAV23nUXv]

jchavannes answered 2020-01-10T22:12:35Z
1 votes

使用[https://github.com/dustin/go-humanize] ..它有很多帮手来处理这些事情。 除字节外还包括MiB,MB等东西。

abourget answered 2020-01-10T22:12:55Z
0 votes

包人性化可以做魔术! 请在此处参阅此软件包的文档。 要使用此软件包,请先使用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
Steffi Keran Rani J answered 2020-01-10T22:13:33Z
-2 votes

如果您不想使用库(无论出于何种原因),我都将其取消。 它似乎可以工作,并且可以使用任何指定的符文作为分隔符:

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发布的解决方案,并欢迎任何可以让我的作品完美运行的人进行任何编辑。

leylandski answered 2020-01-10T22:13:58Z
-2 votes
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]
}
Kiril Minkov answered 2020-01-10T22:14:13Z
translate from https://stackoverflow.com:/questions/13020308/how-to-fmt-printf-an-integer-with-thousands-comma