Memformat string Go tanpa mencetak?

381

Apakah ada cara sederhana untuk memformat string di Go tanpa mencetak string?

Dapat saya lakukan:

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

Tetapi saya ingin string yang diformat dikembalikan daripada dicetak sehingga saya dapat memanipulasinya lebih lanjut.

Saya juga bisa melakukan sesuatu seperti:

s := "foo: " + bar

Tetapi ini menjadi sulit untuk dibaca ketika format string itu rumit, dan rumit ketika satu atau banyak bagian bukan string dan harus dikonversi terlebih dahulu, seperti

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

Apakah ada cara yang lebih sederhana untuk melakukan ini?

Carnegie
sumber

Jawaban:

465

Sprintf adalah apa yang Anda cari.

Contoh

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

Anda juga dapat melihatnya sedang digunakan dalam contoh Kesalahan sebagai bagian dari "A Tour of Go."

return fmt.Sprintf("at %v, %s", e.When, e.What)
Sonia
sumber
6
apakah huruf setelah% penting? Mungkinkah% y dan% q? atau% y dan% y
Filip Bartuzi
17
Huruf itu penting, itu disebut kata kerja, pada dasarnya itu memungkinkan Sprintf tahu apa jenis variabelnya sehingga jika ia menerima 65 dan kata kerjanya adalah% d itu akan mencetak angka 65 tetapi jika kata kerjanya adalah% c itu akan mencetak karakter 'SEBUAH'. Lihat: golang.org/pkg/fmt/#hdr-Printing
redsalt
2
Mengapa disebut Sprintf? S untuk string, f untuk format? Aneh bahwa cetak adalah bagian dari nama fungsi jika fungsi tersebut tidak ditampilkan ke layar. Ini membingungkan saya untuk sementara waktu ...
jcollum
194

1. String sederhana

Untuk string "sederhana" (biasanya yang sesuai dengan garis) solusi paling sederhana adalah menggunakan fmt.Sprintf()dan teman ( fmt.Sprint(), fmt.Sprintln()). Ini analog dengan fungsi tanpa Shuruf starter , tetapi Sxxx()varian ini mengembalikan hasilnya sebagai stringbukannya mencetaknya ke output standar.

Sebagai contoh:

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

Variabel sakan diinisialisasi dengan nilai:

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

Kiat: Jika Anda hanya ingin menggabungkan nilai dari berbagai jenis, Anda mungkin tidak perlu menggunakan secara otomatis Sprintf()(yang memerlukan format string) seperti Sprint()ini. Lihat contoh ini:

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

Untuk penggabungan hanya strings, Anda juga dapat menggunakan strings.Join()tempat Anda dapat menentukan pemisah khusus string(untuk ditempatkan di antara string untuk bergabung).

Cobalah ini di Go Playground .

2. String kompleks (dokumen)

Jika string yang Anda coba buat lebih kompleks (mis. Pesan email multi-baris), fmt.Sprintf()menjadi kurang mudah dibaca dan kurang efisien (terutama jika Anda harus melakukannya berkali-kali).

Untuk ini perpustakaan standar menyediakan paket text/templatedan html/template. Paket-paket ini mengimplementasikan templat yang digerakkan oleh data untuk menghasilkan keluaran teks. html/templateadalah untuk menghasilkan output HTML yang aman terhadap injeksi kode. Ini menyediakan antarmuka yang sama dengan paket text/templatedan harus digunakan alih-alih text/templatesetiap kali output HTML.

Menggunakan templatepaket pada dasarnya mengharuskan Anda untuk menyediakan template statis dalam bentuk stringnilai (yang mungkin berasal dari file dalam hal ini Anda hanya memberikan nama file) yang mungkin berisi teks statis, dan tindakan yang diproses dan dieksekusi ketika mesin memproses templat dan menghasilkan output.

Anda dapat memberikan parameter yang disertakan / diganti dalam template statis dan yang dapat mengontrol proses pembuatan output. Bentuk khas dari parameter tersebut adalah structs dan mapnilai yang mungkin bersarang.

Contoh:

Misalnya, Anda ingin membuat pesan email yang terlihat seperti ini:

Hi [name]!

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

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

Untuk menghasilkan badan pesan email seperti ini, Anda dapat menggunakan templat statis berikut:

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

Dan berikan data seperti ini untuk mengeksekusinya:

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

Biasanya output dari template ditulis ke io.Writer, jadi jika Anda ingin hasilnya sebagai string, buat dan tulis ke bytes.Buffer(yang mengimplementasikan io.Writer). Menjalankan template dan mendapatkan hasilnya sebagai 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()

Ini akan menghasilkan output yang diharapkan:

Hi Bob!

Your account is ready, your user name is: bob92

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

Cobalah di Go Playground .

Juga mencatat bahwa sejak Go 1.10, lebih baru, alternatif yang lebih cepat, lebih khusus tersedia untuk bytes.Bufferyang: strings.Builder. Penggunaannya sangat mirip:

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

Coba yang ini di Playground Go .

Catatan: Anda juga dapat menampilkan hasil eksekusi template jika Anda berikan os.Stdoutsebagai target (yang juga mengimplementasikan io.Writer):

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

Ini akan menulis hasilnya langsung ke os.Stdout. Coba ini di Go Playground .

icza
sumber
2

Dalam kasus Anda, Anda perlu menggunakan Sprintf () untuk string format.

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

Format Sprintf menurut penentu format dan mengembalikan string yang dihasilkan.

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

Output Anda akan:

Selamat Pagi, Ini John dan saya tinggal di sini selama 20 tahun terakhir.

Kabeer Shaikh
sumber
0

Fungsi fmt.SprintF mengembalikan sebuah string dan Anda dapat memformat string dengan cara yang sama seperti yang Anda miliki dengan fmt.PrintF

Mo-Gang
sumber
0

Kami dapat mengkustomisasi tipe String baru melalui define new Typedengan Formatdukungan.

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
sumber