Форматировать строку Go без печати?

381

Есть ли простой способ отформатировать строку в Go без печати строки?

Я могу сделать:

bar := "bar"
fmt.Printf("foo: %s", bar)

Но я хочу, чтобы отформатированная строка возвращалась, а не печаталась, чтобы я мог манипулировать ею дальше.

Я также мог бы сделать что-то вроде:

s := "foo: " + bar

Но это становится трудным для чтения, когда строка формата является сложной, и громоздкой, когда одна или несколько частей не являются строками и должны быть сначала преобразованы, как

i := 25
s := "foo: " + strconv.Itoa(i)

Есть ли более простой способ сделать это?

Карнеги
источник

Ответы:

465

Sprintf - это то, что вы ищете.

пример

fmt.Sprintf("foo: %s", bar)

Вы также можете увидеть его в примере с ошибками как часть «Тур по Го».

return fmt.Sprintf("at %v, %s", e.When, e.What)
Соня
источник
6
имеет значение буква после%? Это могут быть% y и% q? или% y и% y
Филипп Бартузи
17
Буква имеет значение, она называется глаголом, в основном она позволяет Sprintf узнать, к какому типу относится переменная, так что если она получит 65 и глагол% d, то будет напечатано число 65, но если глагол% c, то будет напечатан символ 'A'. См .: golang.org/pkg/fmt/#hdr-Printing
redsalt
2
Почему это называется Sprintf? S для строки, F для формата? Странно, что print является частью имени функции, если функция не выводится на экран. Это сбило меня с
толку
194

1. Простые строки

Для «простых» строк (как правило, то, что вписывается в строку) самое простое решение - это использование fmt.Sprintf()friends ( fmt.Sprint(), fmt.Sprintln()). Они аналогичны функциям без начальной Sбуквы, но эти Sxxx()варианты возвращают результат stringвместо того, чтобы выводить его на стандартный вывод.

Например:

s := fmt.Sprintf("Hi, my name is %s and I'm %d years old.", "Bob", 23)

Переменная sбудет инициализирована со значением:

Hi, my name is Bob and I'm 23 years old.

Подсказка: Если вы просто хотите объединить значения разных типов, вам может не потребоваться автоматически использовать Sprintf()(что требует строки формата), как это Sprint()делает именно это. Смотрите этот пример:

i := 23
s := fmt.Sprint("[age:", i, "]") // s will be "[age:23]"

Для объединения только strings, вы также можете использовать, strings.Join()где вы можете указать пользовательский разделитель string(для размещения между строками для соединения).

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

2. Сложные строки (документы)

Если строка, которую вы пытаетесь создать, является более сложной (например, многострочное сообщение электронной почты), она fmt.Sprintf()становится менее читаемой и менее эффективной (особенно если вам приходится делать это много раз).

Для этого стандартная библиотека предоставляет пакеты text/templateи html/template. Эти пакеты реализуют управляемые данными шаблоны для генерации текстового вывода. html/templateпредназначен для генерации HTML-вывода, безопасного против внедрения кода. Он предоставляет тот же интерфейс, что и пакет, text/templateи его следует использовать вместо text/templateкаждого вывода HTML.

Использование templateпакетов в основном требует предоставления статического шаблона в форме stringзначения (которое может исходить из файла, в этом случае вы указываете только имя файла), которое может содержать статический текст, а также действия, которые обрабатываются и выполняются, когда Движок обрабатывает шаблон и генерирует вывод.

Вы можете предоставить параметры, которые включены / заменены в статическом шаблоне и которые могут управлять процессом генерации вывода. Типичная форма таких параметров - это structs и mapзначения, которые могут быть вложенными.

Пример:

Например, допустим, вы хотите генерировать сообщения электронной почты, которые выглядят так:

Hi [name]!

Your account is ready, your user name is: [user-name]

You have the following roles assigned:
[role#1], [role#2], ... [role#n]

Чтобы сгенерировать подобные сообщения электронной почты, вы можете использовать следующий статический шаблон:

const emailTmpl = `Hi {{.Name}}!

Your account is ready, your user name is: {{.UserName}}

You have the following roles assigned:
{{range $i, $r := .Roles}}{{if $i}}, {{end}}{{.}}{{end}}
`

И предоставьте данные для его выполнения:

data := map[string]interface{}{
    "Name":     "Bob",
    "UserName": "bob92",
    "Roles":    []string{"dbteam", "uiteam", "tester"},
}

Обычно выходные шаблоны записываются в io.Writer, поэтому, если вы хотите получить результат как string, создайте и запишите в bytes.Buffer(который реализует io.Writer). Выполнение шаблона и получение результата в виде string:

t := template.Must(template.New("email").Parse(emailTmpl))
buf := &bytes.Buffer{}
if err := t.Execute(buf, data); err != nil {
    panic(err)
}
s := buf.String()

Это приведет к ожидаемому результату:

Hi Bob!

Your account is ready, your user name is: bob92

You have the following roles assigned:
dbteam, uiteam, tester

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

Отметим также , что Go 1.10 новее, быстрее, более специализированы альтернатива доступна , bytes.Bufferкоторая является: strings.Builder. Использование очень похоже:

builder := &strings.Builder{}
if err := t.Execute(builder, data); err != nil {
    panic(err)
}
s := builder.String()

Попробуйте это на Go Playground .

Примечание: вы также можете отобразить результат выполнения шаблона, если указали os.Stdoutв качестве цели (которая также реализует io.Writer):

t := template.Must(template.New("email").Parse(emailTmpl))
if err := t.Execute(os.Stdout, data); err != nil {
    panic(err)
}

Это запишет результат непосредственно в os.Stdout. Попробуйте это на игровой площадке Go .

icza
источник
2

В вашем случае вам нужно использовать Sprintf () для форматирования строки.

func Sprintf(format string, a ...interface{}) string

Sprintf форматирует в соответствии со спецификатором формата и возвращает полученную строку.

s := fmt.Sprintf("Good Morning, This is %s and I'm living here from last %d years ", "John", 20)

Ваш вывод будет:

Доброе утро, это Джон, и я живу здесь последние 20 лет.

Кабир Шейх
источник
0

Функция fmt.SprintF возвращает строку, и вы можете отформатировать строку точно так же, как и в fmt.PrintF.

Мо-Ган
источник
0

Мы можем настроить новый тип String через define new Typeс Formatподдержкой.

package main

import (
    "fmt"
    "text/template"
    "strings"
)

type String string
func (s String) Format(data map[string]interface{}) (out string, err error) {
    t := template.Must(template.New("").Parse(string(s)))
    builder := &strings.Builder{}
    if err = t.Execute(builder, data); err != nil {
        return
    }
    out = builder.String()
    return
}


func main() {
    const tmpl = `Hi {{.Name}}!  {{range $i, $r := .Roles}}{{if $i}}, {{end}}{{.}}{{end}}`
    data := map[string]interface{}{
        "Name":     "Bob",
        "Roles":    []string{"dbteam", "uiteam", "tester"},
    }

    s ,_:= String(tmpl).Format(data)
    fmt.Println(s)
}
ahuigo
источник