Định dạng một chuỗi Go mà không in?


381

Có cách nào đơn giản để định dạng chuỗi trong Go mà không in chuỗi không?

Tôi có thể làm:

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

Nhưng tôi muốn chuỗi được định dạng trả về thay vì được in để tôi có thể thao tác chuỗi hơn nữa.

Tôi cũng có thể làm một cái gì đó như:

s := "foo: " + bar

Nhưng điều này trở nên khó đọc khi chuỗi định dạng phức tạp và cồng kềnh khi một hoặc nhiều phần không phải là chuỗi và phải được chuyển đổi trước, như

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

Có cách nào đơn giản hơn để làm điều này?

Câu trả lời:


465

Sprintf là những gì bạn đang tìm kiếm.

Thí dụ

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

Bạn cũng có thể thấy nó được sử dụng trong ví dụ Lỗi như một phần của "Chuyến tham quan".

return fmt.Sprintf("at %v, %s", e.When, e.What)

6
thư sau% có vấn đề? Nó có thể là% y và% q? hoặc% y và% y
Filip Bartuzi

17
Chữ cái có vấn đề, nó được gọi là động từ, về cơ bản nó cho Sprintf biết loại biến là gì để nếu nó nhận được 65 và động từ là% d, nó sẽ in số 65 nhưng nếu động từ là% c thì nó sẽ in ký tự 'A'. Xem: golang.org/pkg/fmt/#hdr-Printing
redsalt

2
Tại sao lại gọi là Sprintf? S cho chuỗi, f cho định dạng? Thật kỳ lạ khi in là một phần của tên hàm nếu hàm không xuất ra màn hình. Điều này đã làm tôi bối rối trong một thời gian ...
jcollum

194

1. Chuỗi đơn giản

Đối với các chuỗi "đơn giản" (thường là những gì phù hợp với một dòng), giải pháp đơn giản nhất là sử dụng fmt.Sprintf()và bạn bè ( fmt.Sprint(), fmt.Sprintln()). Chúng tương tự như các hàm không có Schữ cái khởi động , nhưng các Sxxx()biến thể này trả về kết quả là mộtstring thay vì in chúng ra đầu ra tiêu chuẩn.

Ví dụ:

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

Biến ssẽ được khởi tạo với giá trị:

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

Mẹo: Nếu bạn chỉ muốn nối các giá trị của các loại khác nhau, bạn có thể không cần tự động sử dụng Sprintf()(yêu cầu một chuỗi định dạng) như Sprint()chính xác điều này. Xem ví dụ này:

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

Để chỉ ghép nối strings, bạn cũng có thể sử dụng strings.Join()nơi bạn có thể chỉ định một dấu tách tùy chỉnh string(được đặt giữa các chuỗi để nối).

Hãy thử những thứ này trên Sân chơi Go .

2. Chuỗi phức (tài liệu)

Nếu chuỗi bạn đang cố tạo phức tạp hơn (ví dụ: thư email nhiều dòng), fmt.Sprintf()sẽ trở nên ít đọc hơn và kém hiệu quả hơn (đặc biệt là nếu bạn phải làm điều này nhiều lần).

Đối với điều này, thư viện tiêu chuẩn cung cấp các gói text/templatehtml/template. Các gói này triển khai các mẫu dựa trên dữ liệu để tạo đầu ra văn bản. html/templatelà để tạo đầu ra HTML an toàn chống lại việc tiêm mã. Nó cung cấp giao diện giống như gói text/templatevà nên được sử dụng thay vìtext/template bất cứ khi nào đầu ra là HTML.

Việc sử dụng các templategói về cơ bản đòi hỏi bạn phải cung cấp một mẫu tĩnh ở dạngstring giá trị (có thể bắt nguồn từ một tệp trong trường hợp bạn chỉ cung cấp tên tệp) có thể chứa văn bản tĩnh và các hành động được xử lý và thực thi khi công cụ xử lý mẫu và tạo đầu ra.

Bạn có thể cung cấp các tham số được bao gồm / thay thế trong mẫu tĩnh và có thể kiểm soát quá trình tạo đầu ra. Dạng điển hình của các tham số như vậy là structs và mapcác giá trị có thể được lồng vào nhau.

Thí dụ:

Ví dụ: giả sử bạn muốn tạo thư email giống như thế này:

Hi [name]!

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

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

Để tạo các nội dung thư email như thế này, bạn có thể sử dụng mẫu tĩnh sau:

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

Và cung cấp dữ liệu như thế này để thực hiện nó:

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

Thông thường đầu ra của các mẫu được ghi vào một io.Writer, vì vậy nếu bạn muốn kết quả là a string, hãy tạo và ghi vào một bytes.Buffer(thực hiện io.Writer). Thực hiện mẫu và nhận kết quả là 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()

Điều này sẽ dẫn đến đầu ra dự kiến:

Hi Bob!

Your account is ready, your user name is: bob92

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

Hãy thử nó trên Sân chơi Go .

Cũng lưu ý rằng kể từ Go 1.10, một lựa chọn mới hơn, nhanh hơn, chuyên dụng hơn có sẵn bytes.Bufferlà : strings.Builder. Cách sử dụng rất giống nhau:

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

Hãy thử cái này trên Sân chơi Go .

Lưu ý: bạn cũng có thể hiển thị kết quả thực hiện mẫu nếu bạn cung cấp os.Stdoutlàm mục tiêu (cũng thực hiện io.Writer):

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

Điều này sẽ viết kết quả trực tiếp đến os.Stdout. Hãy thử điều này trên Sân chơi Go .


2

Trong trường hợp của bạn, bạn cần sử dụng Sprintf () cho chuỗi định dạng.

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

Định dạng Sprintf theo một chỉ định định dạng và trả về chuỗi kết quả.

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

Đầu ra của bạn sẽ là:

Chào buổi sáng, đây là John và tôi sống ở đây từ 20 năm trước.


0

Hàm fmt.SprintF trả về một chuỗi và bạn có thể định dạng chuỗi theo cách giống như bạn có với fmt.PrintF


0

Chúng ta có thể tùy chỉnh Một loại Chuỗi mới qua define new Typevới Formatsự ủng hộ.

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)
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.