Formatar uma sequência Go sem imprimir?

381

Existe uma maneira simples de formatar uma sequência no Go sem imprimir a sequência?

Eu posso fazer:

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

Mas quero que a string formatada seja retornada em vez de impressa para que eu possa manipulá-la ainda mais.

Eu também poderia fazer algo como:

s := "foo: " + bar

Mas isso fica difícil de ler quando a sequência de formatação é complexa e complicada quando uma ou várias partes não são sequências e precisam ser convertidas primeiro, como

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

Existe uma maneira mais simples de fazer isso?

Carnegie
fonte

Respostas:

465

Sprintf é o que você está procurando.

Exemplo

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

Você também pode vê-lo em uso no exemplo de Erros como parte de "Um tour de Go".

return fmt.Sprintf("at %v, %s", e.When, e.What)
Sonia
fonte
6
letra após% importa? Poderia ser% y e% q? ou% y e% y
Filip Bartuzi 28/03
17
A letra importa, é chamada de verbo, basicamente permite que a Sprintf saiba que tipo de variável é, de modo que, se receber 65 e o verbo for% d, imprimirá o número 65, mas se o verbo for% c, imprimirá o caractere 'UMA'. Veja: golang.org/pkg/fmt/#hdr-Printing
redsalt
2
Por que é chamado Sprintf? S para string, f para formato? É estranho que a impressão faça parte do nome da função se a função não for exibida na tela. Isso me deixou perplexo por um tempo ...
jcollum 07/04
194

1. Strings simples

Para seqüências de caracteres "simples" (normalmente o que se encaixa em uma linha), a solução mais simples é usar fmt.Sprintf()e amigos ( fmt.Sprint(), fmt.Sprintln()). Elas são análogas às funções sem a Sletra inicial , mas essas Sxxx()variantes retornam o resultado como a em stringvez de imprimi-las na saída padrão.

Por exemplo:

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

A variável sserá inicializada com o valor:

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

Dica: Se você deseja concatenar valores de tipos diferentes, talvez não seja necessário usar automaticamente Sprintf()(o que requer uma cadeia de formatação) o que Sprint()é exatamente isso. Veja este exemplo:

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

Para concatenar apenas strings, você também pode usar strings.Join()onde você pode especificar um separador personalizado string(a ser colocado entre as seqüências de caracteres a serem unidas).

Experimente estes no Go Playground .

2. Strings complexas (documentos)

Se a string que você está tentando criar é mais complexa (por exemplo, uma mensagem de email com várias linhas), fmt.Sprintf()torna-se menos legível e menos eficiente (especialmente se você precisar fazer isso várias vezes).

Para isso, a biblioteca padrão fornece os pacotes text/templatee html/template. Esses pacotes implementam modelos orientados a dados para gerar saída de texto. html/templateé para gerar saída HTML segura contra injeção de código. Ele fornece a mesma interface que o pacote text/templatee deve ser usado em vez de text/templatesempre que a saída for HTML.

O uso dos templatepacotes exige basicamente que você forneça um modelo estático na forma de um stringvalor (que pode ser originário de um arquivo; nesse caso, você fornece apenas o nome do arquivo), que pode conter texto estático e ações que são processadas e executadas quando o arquivo O mecanismo processa o modelo e gera a saída.

Você pode fornecer parâmetros que são incluídos / substituídos no modelo estático e que podem controlar o processo de geração de saída. A forma típica de tais parâmetros éstruct s e mapvalores que podem ser aninhados.

Exemplo:

Por exemplo, digamos que você queira gerar mensagens de email com esta aparência:

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 gerar corpos de mensagens de email como este, você pode usar o seguinte modelo estático:

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

E forneça dados como este para executá-lo:

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

Normalmente, a saída dos modelos é gravada em um io.Writer, portanto, se você deseja o resultado como a string, crie e grave em um bytes.Buffer(que implementa io.Writer). Executando o modelo e obtendo o resultado comostring :

t := template.Must(template.New("email").Parse(emailTmpl))
buf := &bytes.Buffer{}
if err := t.Execute(buf, data); err != nil {
    panic(err)
}
s := buf.String()

Isso resultará na saída esperada:

Hi Bob!

Your account is ready, your user name is: bob92

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

Experimente no Go Playground .

Observe também que, desde Go 1,10, uma alternativa mais recente, mais rápido, mais especializada está disponível para bytes.Buffero que é: strings.Builder. O uso é muito semelhante:

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

Experimente este no Go Playground .

Nota: você também pode exibir o resultado da execução de um modelo se fornecer os.Stdoutcomo destino (que também implementa io.Writer):

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

Isso gravará o resultado diretamente em os.Stdout. Tente isso no Playground Go .

icza
fonte
2

No seu caso, você precisa usar Sprintf () para formatar string.

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

Sprintf formata de acordo com um especificador de formato e retorna a sequência resultante.

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

Sua saída será:

Bom dia, este é John e moro aqui nos últimos 20 anos.

Kabeer Shaikh
fonte
0

A função fmt.SprintF retorna uma string e você pode formatar a string da mesma maneira que faria com fmt.PrintF

Mo-Gang
fonte
0

Podemos personalizar um novo tipo de String via define new Typecom Formatsuporte.

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
fonte