¿Formatear una cadena Go sin imprimir?

381

¿Hay una manera simple de formatear una cadena en Go sin imprimir la cadena?

Puedo hacer:

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

Pero quiero que la cadena formateada se devuelva en lugar de imprimirse para poder manipularla más.

También podría hacer algo como:

s := "foo: " + bar

Pero esto se vuelve difícil de leer cuando la cadena de formato es compleja y engorrosa cuando una o muchas de las partes no son cadenas y tienen que convertirse primero, como

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

¿Hay una manera más simple de hacer esto?

Carnegie
fuente

Respuestas:

465

Sprintf es lo que estás buscando.

Ejemplo

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

También puede verlo en uso en el ejemplo de Errores como parte de "Un recorrido por Go".

return fmt.Sprintf("at %v, %s", e.When, e.What)
Sonia
fuente
66
¿Importa la letra después de%? ¿Podría ser% y y% q? o% y y% y
Filip Bartuzi
17
La letra sí importa, se llama verbo, básicamente le permite a Sprintf saber de qué tipo es la variable, de modo que si recibe 65 y el verbo es% d, imprimirá el número 65, pero si el verbo es% c, imprimirá el carácter 'UNA'. Ver: golang.org/pkg/fmt/#hdr-Printing
redsalt
2
¿Por qué se llama Sprintf? S para cadena, f para formato? Es extraño que imprimir sea parte del nombre de la función si la función no sale a la pantalla. Esto me ha
dejado
194

1. Cuerdas simples

Para cadenas "simples" (típicamente lo que cabe en una línea) la solución más simple es usar fmt.Sprintf()y amigos ( fmt.Sprint(), fmt.Sprintln()). Estas son análogas a las funciones sin la Sletra de inicio , pero estas Sxxx()variantes devuelven el resultado en stringlugar de imprimirlas en la salida estándar.

Por ejemplo:

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

La variable sse inicializará con el valor:

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

Sugerencia: Si solo desea concatenar valores de diferentes tipos, es posible que no necesite usarlos automáticamente Sprintf()(lo que requiere una cadena de formato) como Sprint()hace exactamente esto. Ver este ejemplo:

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

Para concatenar solo strings, también puede usar strings.Join()donde puede especificar un separador personalizado string(que se colocará entre las cadenas para unir).

Pruébalos en Go Playground .

2. Cadenas complejas (documentos)

Si la cadena que está intentando crear es más compleja (por ejemplo, un mensaje de correo electrónico de varias líneas), se fmt.Sprintf()vuelve menos legible y menos eficiente (especialmente si tiene que hacerlo muchas veces).

Para esto, la biblioteca estándar proporciona los paquetes text/templatey html/template. Estos paquetes implementan plantillas basadas en datos para generar resultados textuales. html/templatees para generar salida HTML segura contra la inyección de código. Proporciona la misma interfaz que el paquete text/templatey debe usarse en lugar de text/templatesiempre que la salida sea HTML.

El uso de los templatepaquetes básicamente requiere que proporcione una plantilla estática en forma de un stringvalor (que puede ser originario de un archivo en cuyo caso solo proporciona el nombre del archivo) que puede contener texto estático y acciones que se procesan y ejecutan cuando el El motor procesa la plantilla y genera la salida.

Puede proporcionar parámetros que se incluyen / sustituyen en la plantilla estática y que pueden controlar el proceso de generación de salida. La forma típica de tales parámetros son structsy mapvalores que pueden estar anidados.

Ejemplo:

Por ejemplo, supongamos que desea generar mensajes de correo electrónico que se vean así:

Hi [name]!

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

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

Para generar cuerpos de mensajes de correo electrónico como este, puede usar la siguiente plantilla estática:

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

Y proporcione datos como este para ejecutarlo:

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

Normalmente, la salida de las plantillas se escribe en un io.Writer, así que si desea el resultado como a string, cree y escriba en un bytes.Buffer(que implementa io.Writer). Ejecutando la plantilla y obteniendo el resultado como 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()

Esto dará como resultado el resultado esperado:

Hi Bob!

Your account is ready, your user name is: bob92

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

Pruébalo en Go Playground .

También tenga en cuenta que, dado Go 1.10, una alternativa más nuevo, más rápido, más especializado está disponible para los bytes.Bufferque es: strings.Builder. El uso es muy similar:

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

Prueba este en Go Playground .

Nota: también puede mostrar el resultado de una ejecución de plantilla si proporciona os.Stdoutcomo destino (que también implementa io.Writer):

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

Esto escribirá el resultado directamente en os.Stdout. Prueba esto en Go Playground .

icza
fuente
2

En su caso, debe usar Sprintf () para formatear la cadena.

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

Sprintf formatea de acuerdo con un especificador de formato y devuelve la cadena resultante.

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

Su salida será:

Buenos días, soy John y vivo aquí desde hace 20 años.

Kabeer Shaikh
fuente
0

La función fmt.SprintF devuelve una cadena y puede formatear la cadena de la misma manera que lo haría con fmt.PrintF

Mo-Gang
fuente
0

Podemos personalizar un nuevo tipo de cadena a través define new Typede Formatsoporte.

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
fuente