Chuyển đến nội dung chính
Mô-đun 8 · Bài học 1 trong tổng số 536/50 trong khóa học~10 min
Bài học theo mô-đun (1/5)

`fmt` nâng cao

Gói fmt là cổng vào tất cả I/O văn bản trong Go. Biết rõ về nó có nghĩa là viết nhật ký có thể đọc được, các lỗi thông tin và định dạng các giá trị một cách thành ngữ mà không cần dùng đến các thư viện bên ngoài.

Bốn gia đình

fmt hiển thị các hàm trong bốn họ, được phân biệt bằng tiền tố:

  • Print* → viết vào os.Stdout (Print, Println, Printf).
  • Fprint* → ghi vào io.Writer bất kỳ (Fprintln(os.Stderr, ...), Fprintf(file, ...)).
  • Sprint* → trả về chuỗi không in (Sprintf, Sprintln).
  • Errorf → tạo một error được định dạng.
Go
fmt.Println("hello")                       // stdout
fmt.Fprintln(os.Stderr, "boom")            // stderr
s := fmt.Sprintf("[%d]", 42)               // "[42]"
err := fmt.Errorf("parse %q: %w", in, base) // wrapped error

Sự khác biệt giữa Print và Println chỉ là khoảng cách giữa các đối số và dòng mới ở cuối: Println luôn thêm \n, Print không, Printf tuân theo định dạng.

Động từ chính

Động từNó làm gì
%vGiá trị mặc định (gọi String() nếu được triển khai).
%+vCấu trúc với tên trường.
%#vCú pháp Go (hữu ích cho việc gỡ lỗi và chụp ảnh nhanh).
%TLoại động của giá trị.
%dSố nguyên trong cơ số 10.
%b %o %x %XNhị phân, bát phân, thập lục phân (chữ thường/chữ hoa).
%f %e %gĐiểm nổi: cố định, hàm mũ, nhỏ gọn.
%sChuỗi (hoặc error/Stringer).
%qChuỗi được trích dẫn kiểu Go (bao gồm các ký tự thoát).
%cKý tự Unicode (rune).
%pCon trỏ ở dạng thập lục phân.
%wLỗi gói (chỉ bên trong Errorf).
Go
type Point struct{ X, Y int }
p := Point{1, 2}
fmt.Printf("%v\n", p)   // {1 2}
fmt.Printf("%+v\n", p)  // {X:1 Y:2}
fmt.Printf("%#v\n", p)  // main.Point{X:1, Y:2}
fmt.Printf("%T\n", p)   // main.Point

Chiều rộng, độ chính xác, cờ

Động từ dạng số và chuỗi chấp nhận độ rộng và độ chính xác:

  • %6d → chiều rộng tối thiểu 6, phần đệm bên trái có khoảng trắng.
  • %-6d → chiều rộng 6, phần đệm bên phải (cờ -).
  • %06d → chiều rộng 6, đệm bằng 0 (cờ 0).
  • %.2f → độ chính xác 2 chữ số thập phân.
  • %8.2f → chiều rộng 8, độ chính xác 2.
Go
fmt.Printf("|%6d|\n", 42)    // |    42|
fmt.Printf("|%-6d|\n", 42)   // |42    |
fmt.Printf("|%06d|\n", 42)   // |000042|
fmt.Printf("|%8.2f|\n", 3.14)// |    3.14|

Đối với các bảng đã căn chỉnh, chiều rộng cố định + %-...s cho cột văn bản là mẫu đơn giản nhất trước khi chuyển sang text/tabwriter.

%w và gói lỗi

%w rất đặc biệt: nó chỉ hoạt động bên trong fmt.Errorf và giữ nguyên lỗi ban đầu trong chuỗi, để errors.Is và errors.As có thể thực hiện được.

Go
if _, err := os.Open(path); err != nil {
    return fmt.Errorf("read config %q: %w", path, err)
}

Sử dụng %v thay vì %w in thông báo lỗi nhưng làm đứt chuỗi: không mở gói, không errors.Is. Quy tắc: nếu bạn muốn người gọi có thể phân biệt loại hoặc nhận dạng lỗi, hãy sử dụng %w (chỉ một lỗi cho mỗi Errorf).

Bài tập

tập thể dục#go.m8.l1.e1
Nỗ lực: 0Đang tải…

Sử dụng fmt.Sprintf để định dạng chuỗi '[42]' từ số nguyên 42, sau đó in nó.

Đang tải trình chỉnh sửa…

Giải pháp khả dụng sau 3 lần thử

tập thể dục#go.m8.l1.e2
Nỗ lực: 0Đang tải…

In cấu trúc p với động từ %+v (nó phải bao gồm tên trường).

Đang tải trình chỉnh sửa…

Giải pháp khả dụng sau 3 lần thử

Quiz#go.m8.l1.e3
Sẵn sàng

Động từ nào, được sử dụng trong fmt.Errorf, bảo toàn chuỗi lỗi cho các lỗi.Is/As?

Go
err := fmt.Errorf("read %q: ???", path, base)
Tùy chọn trả lời