Formater une chaîne Go sans imprimer?

381

Existe-t-il un moyen simple de formater une chaîne dans Go sans imprimer la chaîne?

Je peux faire:

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

Mais je veux que la chaîne formatée soit retournée plutôt qu'imprimée pour que je puisse la manipuler davantage.

Je pourrais aussi faire quelque chose comme:

s := "foo: " + bar

Mais cela devient difficile à lire lorsque la chaîne de format est complexe, et encombrant lorsqu'une ou plusieurs des parties ne sont pas des chaînes et doivent être converties en premier, comme

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

Existe-t-il un moyen plus simple de procéder?

Carnegie
la source

Réponses:

465

Sprintf est ce que vous recherchez.

Exemple

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

Vous pouvez également le voir en cours d'utilisation dans l' exemple Erreurs dans le cadre de "A Tour of Go."

return fmt.Sprintf("at %v, %s", e.When, e.What)
Sonia
la source
6
la lettre après% est-elle importante? Serait-ce% y et% q? ou% y et% y
Filip Bartuzi
17
La lettre est importante, elle s'appelle un verbe, fondamentalement, elle permet à Sprintf de savoir de quel type est la variable, de sorte que si elle reçoit 65 et que le verbe est% d, elle affichera le nombre 65 mais si le verbe est% c, elle imprimera le caractère 'UNE'. Voir: golang.org/pkg/fmt/#hdr-Printing
redsalt le
2
Pourquoi s'appelle-t-il Sprintf? S pour chaîne, f pour format? Il est étrange que l'impression fasse partie du nom de la fonction si la fonction ne s'affiche pas à l'écran. Cela m'a perplexe pendant un certain temps ...
jcollum
194

1. Chaînes simples

Pour les chaînes "simples" (généralement ce qui tient dans une ligne), la solution la plus simple consiste à utiliser fmt.Sprintf()and friends ( fmt.Sprint(), fmt.Sprintln()). Celles-ci sont analogues aux fonctions sans la Slettre de démarrage , mais ces Sxxx()variantes renvoient le résultat au stringlieu de les imprimer sur la sortie standard.

Par exemple:

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

La variable ssera initialisée avec la valeur:

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

Astuce: Si vous souhaitez simplement concaténer des valeurs de différents types, vous n'aurez peut-être pas besoin d'utiliser automatiquement Sprintf()(ce qui nécessite une chaîne de formatage) comme c'est Sprint()exactement le cas. Voir cet exemple:

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

Pour concaténer uniquement des strings, vous pouvez également utiliser strings.Join()où vous pouvez spécifier un séparateur personnalisé string(à placer entre les chaînes à joindre).

Essayez-les sur le Go Playground .

2. Chaînes complexes (documents)

Si la chaîne que vous essayez de créer est plus complexe (par exemple un e-mail multiligne), elle fmt.Sprintf()devient moins lisible et moins efficace (surtout si vous devez le faire plusieurs fois).

Pour cela, la bibliothèque standard fournit les packages text/templateet html/template. Ces packages implémentent des modèles basés sur les données pour générer une sortie textuelle. html/templatesert à générer une sortie HTML sûre contre l'injection de code. Il fournit la même interface que le package text/templateet doit être utilisé à la place de text/templatechaque fois que la sortie est HTML.

L'utilisation des templatepackages nécessite essentiellement que vous fournissiez un modèle statique sous la forme d'une stringvaleur (qui peut provenir d'un fichier auquel cas vous ne fournissez que le nom du fichier) qui peut contenir du texte statique et des actions qui sont traitées et exécutées lorsque le Le moteur traite le modèle et génère la sortie.

Vous pouvez fournir des paramètres qui sont inclus / substitués dans le modèle statique et qui peuvent contrôler le processus de génération de sortie. La forme typique de ces paramètres est structs et les mapvaleurs qui peuvent être imbriquées.

Exemple:

Par exemple, supposons que vous souhaitiez générer des e-mails qui ressemblent à ceci:

Hi [name]!

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

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

Pour générer des corps de message électronique comme celui-ci, vous pouvez utiliser le modèle statique suivant:

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

Et fournissez des données comme celle-ci pour l'exécuter:

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

Normalement, la sortie des modèles est écrite dans un io.Writer, donc si vous voulez que le résultat soit un string, créez et écrivez dans un bytes.Buffer(qui implémente io.Writer). Exécuter le modèle et obtenir le résultat sous la forme 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()

Cela se traduira par la sortie attendue:

Hi Bob!

Your account is ready, your user name is: bob92

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

Essayez-le sur le Go Playground .

A noter également que depuis Go 1.10, une nouvelle alternative plus rapide, plus spécialisée est disponible pour ce bytes.Bufferqui est: strings.Builder. L'utilisation est très similaire:

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

Essayez celui-ci sur le Go Playground .

Remarque: vous pouvez également afficher le résultat d'une exécution de modèle si vous fournissez os.Stdoutcomme cible (qui implémente également io.Writer):

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

Cela écrira le résultat directement dans os.Stdout. Essayez ceci sur le Go Playground .

icza
la source
2

Dans votre cas, vous devez utiliser Sprintf () pour la chaîne de format.

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

Sprintf formate selon un spécificateur de format et renvoie la chaîne résultante.

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

Votre sortie sera:

Bonjour, voici John et je vis ici depuis 20 ans.

Kabeer Shaikh
la source
0

La fonction fmt.SprintF renvoie une chaîne et vous pouvez formater la chaîne de la même manière que vous le feriez avec fmt.PrintF

Mo-Gang
la source
0

Nous pouvons personnaliser un nouveau type de chaîne via define new Typeavec Formatsupport.

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
la source