Sformatować ciąg Go bez drukowania?

381

Czy istnieje prosty sposób sformatowania łańcucha w Go bez drukowania łańcucha?

Potrafię:

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

Ale chcę, aby sformatowany ciąg znaków został zwrócony, a nie wydrukowany, aby móc dalej nim manipulować.

Mógłbym również zrobić coś takiego:

s := "foo: " + bar

Ale staje się to trudne do odczytania, gdy łańcuch formatu jest złożony, i kłopotliwy, gdy jedna lub wiele części nie jest łańcuchami i trzeba je najpierw przekonwertować, jak

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

Czy istnieje prostszy sposób na zrobienie tego?

Carnegie
źródło

Odpowiedzi:

465

Sprintf jest tym, czego szukasz.

Przykład

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

Możesz go również zobaczyć w użyciu w przykładzie Błędy w ramach „Podróży po Go”.

return fmt.Sprintf("at %v, %s", e.When, e.What)
Sonia
źródło
6
czy litera po% ma znaczenie? Czy może to być% y i% q? lub% y i% y
Filip Bartuzi
17
Litera ma znaczenie, nazywana jest czasownikiem, po prostu pozwala Sprintf wiedzieć, jakiego typu jest zmienna, więc jeśli otrzyma 65, a czasownik to% d, wypisze liczbę 65, ale jeśli czasownik to% c, wypisze znak 'ZA'. Zobacz: golang.org/pkg/fmt/#hdr-Printing
redsalt
2
Dlaczego nazywa się Sprintf? S dla ciągu, f dla formatu? Dziwne jest to, że print jest częścią nazwy funkcji, jeśli funkcja nie wyświetla się na ekranie. Przez pewien czas mnie to
denerwowało
194

1. Proste ciągi

W przypadku „prostych” ciągów znaków (zazwyczaj pasujących do linii) stosuje się najprostsze rozwiązanie fmt.Sprintf() i znajomych ( fmt.Sprint(), fmt.Sprintln()). Są one analogiczne do funkcji bez Slitery początkowej , ale te Sxxx()warianty zwracają wynik stringzamiast wypisywać je na standardowe wyjście.

Na przykład:

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

Zmienna szostanie zainicjowana wartością:

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

Wskazówka: Jeśli chcesz po prostu połączyć wartości różnych typów, może nie być konieczne automatyczne użycie Sprintf()(co wymaga ciągu formatującego), jak Sprint()to dokładnie robi. Zobacz ten przykład:

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

W przypadku łączenia tylko stringmożesz użyć strings.Join()miejsca, w którym możesz określić niestandardowy separator string(umieszczany między ciągami znaków do połączenia).

Wypróbuj je na Go Playground .

2. Złożone ciągi (dokumenty)

Jeśli ciąg, który próbujesz utworzyć, jest bardziej złożony (np. Wieloliniowa wiadomość e-mail), fmt.Sprintf() staje się mniej czytelny i mniej wydajny (zwłaszcza jeśli musisz to zrobić wiele razy).

W tym celu standardowa biblioteka udostępnia pakiety text/templatei html/template. Pakiety te implementują szablony oparte na danych do generowania tekstu wyjściowego. html/templatesłuży do generowania danych wyjściowych HTML zabezpieczonych przed wstrzyknięciem kodu. Zapewnia ten sam interfejs co pakiet text/templatei powinien być używany zamiast text/templatezawsze, gdy wyjściem jest HTML.

Używając template pakietów zasadniczo wymaga dostarczenia szablonu statycznego w postaci stringwartości (która może pochodzić z pliku, w którym to przypadku podajesz tylko nazwę pliku), która może zawierać tekst statyczny oraz działań, które są przetwarzane i wykonywane, gdy silnik przetwarza szablon i generuje dane wyjściowe.

Możesz podać parametry, które są zawarte / podstawione w szablonie statycznym i które mogą kontrolować proces generowania danych wyjściowych. Typową formą takich parametrów sąstructmap wartości i wartości, które mogą być zagnieżdżone.

Przykład:

Załóżmy na przykład, że chcesz generować wiadomości e-mail wyglądające tak:

Hi [name]!

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

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

Aby wygenerować takie treści wiadomości e-mail, możesz użyć następującego szablonu statycznego:

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

I podaj dane do wykonania:

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

Zwykle dane wyjściowe szablonów są zapisywane do io.Writer, więc jeśli chcesz wynik jako string, utwórz i zapisz do bytes.Buffer(który implementuje io.Writer). Wykonanie szablonu i uzyskanie wyniku jako 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()

Spowoduje to oczekiwany wynik:

Hi Bob!

Your account is ready, your user name is: bob92

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

Wypróbuj na placu zabaw Go .

Należy również pamiętać, że od idź 1.10, nowsze, szybsze, bardziej wyspecjalizowane alternatywą jest dostępny na bytes.Bufferktórym jest: strings.Builder. Użycie jest bardzo podobne:

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

Wypróbuj ten na Play Playground .

Uwaga: możesz również wyświetlić wynik wykonania szablonu, jeśli podasz os.Stdoutjako cel (który również implementuje io.Writer):

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

Spowoduje to zapisanie wyniku bezpośrednio do os.Stdout. Wypróbuj to na placu zabaw Go .

icza
źródło
2

W twoim przypadku musisz użyć Sprintf () do formatu ciągu znaków.

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

Sprintf formatuje zgodnie ze specyfikatorem formatu i zwraca wynikowy ciąg.

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

Twój wynik będzie:

Dzień dobry, to jest John i mieszkam tu od 20 lat.

Kabeer Shaikh
źródło
0

Funkcja fmt.SprintF zwraca ciąg znaków i możesz sformatować ciąg w taki sam sposób, jak w przypadku fmt.PrintF

Mo-Gang
źródło
0

Możemy dostosować nowy typ ciągu za define new Typepomocą Formatwsparcia.

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
źródło