Відформатувати рядок Go без друку?


381

Чи є простий спосіб відформатувати рядок у Go, не надрукувавши рядок?

Я можу зробити:

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

Але я хочу, щоб відформатований рядок повернувся, а не був надрукований, щоб я міг ним маніпулювати далі.

Я також міг би зробити щось на кшталт:

s := "foo: " + bar

Але це стає важко читати, коли рядок формату є складним і громіздким, коли одна або багато частин не є рядками і повинні бути перетворені спочатку, як

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

Чи є простіший спосіб зробити це?

Відповіді:


465

Спринтф - це те, що ви шукаєте.

Приклад

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
Чому його називають Спринтф? S для рядка, f для формату? Дивно, що друк є частиною назви функції, якщо функція не виводиться на екран. Це мене на деякий час
здивувало

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"},
}

Як правило, вихідні шаблони записуються в an io.Writer, тому якщо ви хочете результат як a string, створіть і запишіть у a 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 .

Примітка. Ви також можете відобразити результат виконання шаблону, якщо ви вказали 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 .


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

Ми можемо настроїти новий тип рядка за 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)
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.