Как fmt.Е целое число с разделителями тысяч
10 ответов
я писал библиотека для этого а также несколько других проблем с человеческим представительством.
примеры:
0 -> 0
100 -> 100
1000 -> 1,000
1000000000 -> 1,000,000,000
-100000 -> -100,000
Пример Использования:
fmt.Printf("You owe $%s.\n", humanize.Comma(6582491))
использовать 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
}
я опубликовал фрагмент Go в Github функции для отображения числа (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
на 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]
. Числовое значение '-'
is 45
, в то время как числовое значение цифровых символов '0'..'9'
are 48..57
. Таким образом, символ знака меньше возможных цифр. Поэтому, если мы разделим первый символ (всегда есть хотя бы 1 символ) на '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
оператор заботится о первой необязательной, "неполной" группе, которая меньше 3 цифр, если она существует, и последующей for
цикл обрабатывает остальные, копируя 3 цифры на каждой итерации и добавляя запятую (','
) группировка знак, если есть еще цифры.
вот функция, которая принимает целочисленный и группирующий разделитель и возвращает строку, разделенную указанным разделителем. Я попытался оптимизировать для эффективности, без конкатенации строк или mod/division в плотном цикле. Из моего профилирования это более чем в два раза быстрее, чем гуманизация.Реализация запятых (~680ns против 1642ns) на моем Mac. Я новичок в Go, хотел бы видеть более быстрые реализации!
использование: s: = NumberToString(n int, sep руна)
примеры
иллюстрирует использование другого разделителя ( ' , 'vs''), проверенного с диапазоном значений int.
s:= NumberToString (12345678, ',')
=> "12,345,678"
s:= NumberToString (12345678, ' ')
=> "12 345 678"
s: = NumberToString (-9223372036854775807, ',')
=> "-9,223,372,036,854,775,807"
вот простая функция с использованием regex:
import (
"strconv"
"regexp"
)
func formatCommas(num int) string {
str := strconv.Itoa(num)
re := regexp.MustCompile("(\d+)(\d{3})")
for i := 0; i < (len(str) - 1) / 3; i++ {
str = re.ReplaceAllString(str, ",")
}
return str
}
пример:
fmt.Println(formatCommas(1000))
fmt.Println(formatCommas(-1000000000))
выход:
1,000
-1,000,000,000
использовать 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]
}