Eine Go-Zeichenfolge formatieren, ohne zu drucken?

381

Gibt es eine einfache Möglichkeit, eine Zeichenfolge in Go zu formatieren, ohne die Zeichenfolge zu drucken?

Ich kann:

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

Ich möchte jedoch, dass die formatierte Zeichenfolge zurückgegeben und nicht gedruckt wird, damit ich sie weiter bearbeiten kann.

Ich könnte auch so etwas machen wie:

s := "foo: " + bar

Dies ist jedoch schwierig zu lesen, wenn die Formatzeichenfolge komplex ist, und umständlich, wenn einer oder mehrere der Teile keine Zeichenfolgen sind und zuerst konvertiert werden müssen, wie z

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

Gibt es einen einfacheren Weg, dies zu tun?

Carnegie
quelle

Antworten:

465

Sprintf ist was Sie suchen.

Beispiel

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

Sie können es auch im Fehlerbeispiel als Teil von "A Tour of Go" sehen.

return fmt.Sprintf("at %v, %s", e.When, e.What)
Sonia
quelle
6
Ist ein Buchstabe nach% wichtig? Könnte es% y und% q sein? oder% y und% y
Filip Bartuzi
17
Der Buchstabe spielt eine Rolle, er wird als Verb bezeichnet. Im Grunde lässt er Sprintf wissen, welcher Typ die Variable ist. Wenn er 65 empfängt und das Verb% d ist, wird die Zahl 65 gedruckt, aber wenn das Verb% c ist, wird das Zeichen gedruckt 'EIN'. Siehe: golang.org/pkg/fmt/#hdr-Printing
redsalt
2
Warum heißt es Sprintf? S für String, f für Format? Es ist seltsam, dass der Druck Teil des Funktionsnamens ist, wenn die Funktion nicht auf dem Bildschirm ausgegeben wird. Das hat mich eine Weile verwirrt ...
jcollum
194

1. Einfache Zeichenfolgen

Für "einfache" Zeichenfolgen (normalerweise das, was in eine Zeile passt) ist die einfachste Lösung die Verwendung von fmt.Sprintf()und friends ( fmt.Sprint(), fmt.Sprintln()). Diese sind analog zu den Funktionen ohne Startbuchstaben S, aber diese Sxxx()Varianten geben das Ergebnis als a zurück, stringanstatt sie an die Standardausgabe zu drucken.

Zum Beispiel:

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

Die Variable swird mit dem Wert initialisiert:

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

Tipp: Wenn Sie nur Werte verschiedener Typen verketten möchten, müssen Sie diese möglicherweise nicht automatisch verwenden Sprintf()(für die eine Formatzeichenfolge erforderlich ist) Sprint(). Siehe dieses Beispiel:

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

Wenn Sie nur strings verketten möchten, können Sie auch strings.Join()ein benutzerdefiniertes Trennzeichen angeben string(das zwischen den zu verbindenden Zeichenfolgen platziert werden soll).

Probieren Sie diese auf dem Go Playground aus .

2. Komplexe Zeichenfolgen (Dokumente)

Wenn die Zeichenfolge, die Sie erstellen möchten, komplexer ist (z. B. eine mehrzeilige E-Mail-Nachricht), fmt.Sprintf()wird sie weniger lesbar und weniger effizient (insbesondere, wenn Sie dies häufig tun müssen).

Hierzu stellt die Standardbibliothek die Pakete text/templateund zur Verfügung html/template. Diese Pakete implementieren datengesteuerte Vorlagen zum Generieren von Textausgaben. html/templatedient zum Generieren einer HTML-Ausgabe, die gegen Code-Injection geschützt ist. Es bietet dieselbe Schnittstelle wie das Paket text/templateund sollte anstelle der text/templateHTML-Ausgabe verwendet werden.

Für die Verwendung der templatePakete müssen Sie grundsätzlich eine statische Vorlage in Form eines stringWerts (der möglicherweise aus einer Datei stammt, in diesem Fall geben Sie nur den Dateinamen an) bereitstellen, der statischen Text enthalten kann, sowie Aktionen, die verarbeitet und ausgeführt werden, wenn die Die Engine verarbeitet die Vorlage und generiert die Ausgabe.

Sie können Parameter angeben, die in der statischen Vorlage enthalten / ersetzt sind und die den Prozess der Ausgabegenerierung steuern können. Typische Form solcher Parameter sind structs und mapWerte, die verschachtelt sein können.

Beispiel:

Angenommen, Sie möchten E-Mail-Nachrichten generieren, die folgendermaßen aussehen:

Hi [name]!

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

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

Um solche E-Mail-Nachrichtentexte zu generieren, können Sie die folgende statische Vorlage verwenden:

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

Und stellen Sie Daten wie diese zur Ausführung bereit:

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

Normalerweise wird die Ausgabe von Vorlagen in a geschrieben. io.WriterWenn Sie also das Ergebnis als a möchten string, erstellen und schreiben Sie in a bytes.Buffer(das implementiert io.Writer). Ausführen der Vorlage und Abrufen des Ergebnisses wie folgt 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()

Dies führt zu der erwarteten Ausgabe:

Hi Bob!

Your account is ready, your user name is: bob92

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

Probieren Sie es auf dem Go Playground aus .

Beachten Sie auch, dass seit Go 1.10 eine neuere, schnellere und spezialisiertere Alternative verfügbar bytes.Bufferist : strings.Builder. Die Verwendung ist sehr ähnlich:

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

Probieren Sie diesen auf dem Go Playground aus .

Hinweis: Sie können auch das Ergebnis einer Vorlagenausführung anzeigen, wenn Sie os.Stdoutals Ziel angeben (das auch implementiert wird io.Writer):

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

Dadurch wird das Ergebnis direkt in geschrieben os.Stdout. Versuchen Sie dies auf dem Go-Spielplatz .

icza
quelle
2

In Ihrem Fall müssen Sie Sprintf () für die Formatzeichenfolge verwenden.

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

Sprintf formatiert gemäß einem Formatbezeichner und gibt die resultierende Zeichenfolge zurück.

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

Ihre Ausgabe wird sein:

Guten Morgen, das ist John und ich lebe hier seit 20 Jahren.

Kabeer Shaikh
quelle
0

Die Funktion fmt.SprintF gibt eine Zeichenfolge zurück, und Sie können die Zeichenfolge genauso formatieren, wie Sie es mit fmt.PrintF tun würden

Mo-Gang
quelle
0

Wir können einen neuen String-Typ über define new Typemit FormatUnterstützung anpassen.

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
quelle