Подтвердить что ты не робот

Как fmt.Printf целое число с тысячами запятых

Does Go fmt.Printf поддерживает вывод числа с запятой тысяч?

fmt.Printf("%d", 1000) выводит 1000, какой формат я могу указать для вывода 1,000 вместо?

docs, похоже, не упоминают запятые, и я не мог сразу увидеть что-либо в source.

4b9b3361

Ответ 1

Ни один из глаголов печати fmt не поддерживает тысячи разделителей.

Ответ 2

Я написал библиотеку для этого, а также несколько других проблем с представлением интересов человека.

Примеры результатов:

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

Пример использования:

fmt.Printf("You owe $%s.\n", humanize.Comma(6582491))

Ответ 3

Используйте 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
}

Ответ 4

Я опубликовал фрагмент 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

Ответ 5

Пакет fmt не поддерживает группировку десятичных дробей.

Мы должны реализовать его сами (или использовать уже существующий).

Код

Вот компактное и действительно эффективное решение (см. Объяснение после):

Попробуйте это на игровой площадке Go.

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. Таким образом, символ знака меньше возможных цифр. Таким образом, если мы разделим первый символ (всегда есть хотя бы 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 цифры в каждой итерации и добавляя знак группировки через запятую (',') если есть еще цифры.

Ответ 6

Вот функция, которая принимает разделитель целых чисел и группировки и возвращает строку, ограниченную указанным разделителем. Я попытался оптимизировать эффективность, отсутствие конкатенации строк или mod/division в узком цикле. Из моего профилирования это более чем в два раза быстрее, чем реализация humanize.Commas(~ 680ns против 1642ns) на моем Mac. Я новичок в 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()
}

Ответ 7

Вот простая функция с использованием регулярных выражений:

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

Ответ 8

Используйте https://github.com/dustin/go-humanize.. у него есть куча помощников, чтобы справиться с этими вещами. В дополнение к байтам, как MiB, MB и другие лакомства.

Ответ 9

Если вы не хотите использовать библиотеку (по какой-то причине), я ее сбил. Кажется, что он работает и может использовать любую указанную руну в качестве разделителя:

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, и приветствовать любые изменения от всех, кто может заставить мои работать отлично.

Ответ 10

Гуманизированный пакет может творить чудеса! Обратитесь к документации этого пакета здесь. Чтобы использовать этот пакет, сначала установите его с помощью такого инструмента, как Git SCM. Если вы используете Git Bash, откройте окно оболочки и введите:

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 и т.д. BigComma, Comma, BigCommaf зависят от типа данных BigComma, Comma, BigCommaf вами.

Итак, при запуске этой программы с помощью команды:

go run main.go

Вы увидите вывод, такой как этот:

123,456,789
-1,000,000,000
-100,000.005
100,000

Ответ 11

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]
}