0

Go Zero to Hero - Bài 9: Package fmt - In định dạng và nhập dữ liệu từ console

Package fmt chia làm hai nhóm nhiệm vụ chính: Output (In dữ liệu ra/Định dạng chuỗi) và Input (Đọc dữ liệu vào).


1. Xuất dữ liệu (Output): Bộ 3 quyền lực Print, Println, Printf

fmt.Printfmt.Println

Hai hàm này rất đơn giản:

  • Print: In các giá trị ra màn hình, nối liền nhau.
  • Println: Giống Print nhưng tự động chèn thêm một khoảng trắng giữa các biến và tự động xuống dòng ở cuối.
name := "Hoàng"
age := 21
fmt.Print("Tên:", name, "Tuổi:", age)   // Kết quả: Tên:HoàngTuổi:21
fmt.Println("Tên:", name, "Tuổi:", age) // Kết quả: Tên: Hoàng Tuổi: 21 (và xuống dòng)

fmt.Printf (Print Format): Nghệ thuật định dạng

Đây mới là "trùm cuối". Khi làm Backend, bạn sẽ thường xuyên phải log ra những dòng thông báo kết hợp giữa chuỗi tĩnh và giá trị biến. Nếu dùng Println nối chuỗi bằng dấu phẩy thì code trông rất xấu.

Printf cho phép bạn tạo một chuỗi mẫu (template) với các "chỗ trống" (gọi là verbs hoặc format specifiers), sau đó điền các biến vào những chỗ trống đó.

name := "Hoàng"
role := "Backend Developer"
salary := 2500.50

// Sử dụng Printf để định dạng
fmt.Printf("Xin chào %s! Chức vụ của bạn là %s. Lương: %.2f USD\n", name, role, salary)

Bảng cẩm nang các Verbs (Chỉ mục định dạng) cần nhớ nhất:

Verb Ý nghĩa Ví dụ thực tế
%s Dành cho Chuỗi (String) fmt.Printf("%s", "Hello")
%d Dành cho Số nguyên (Decimal/Integer) fmt.Printf("%d", 100)
%f Dành cho Số thực. Bạn có thể ép số thập phân bằng %.2f (lấy 2 số lẻ) fmt.Printf("%.2f", 3.1415)
%T Siêu hữu ích: In ra Kiểu dữ liệu của biến fmt.Printf("%T", age) -> int
%v (Value) In ra giá trị mặc định của biến, bất kể kiểu gì fmt.Printf("%v", name)

Tuyệt chiêu Debug Struct cho Backend Dev: Sau này khi bạn học đến Struct (để hứng dữ liệu JSON, DB record), việc log ra xem bên trong struct chứa gì là bắt buộc. Hãy nhớ hai verb này:

  • %+v: In ra giá trị kèm theo tên field (tên trường dữ liệu).
  • %#v: In ra giá trị giống hệt cú pháp code Go (rất chi tiết).

2. fmt.Sprintf: Trả về chuỗi thay vì in ra màn hình

Đôi khi, bạn không muốn in thông báo ra màn hình Console, mà bạn muốn lưu thông báo đó vào một biến chuỗi để chèn vào database, gửi qua API response, hay ghi vào file log.

Hàm Sprintf (String Print Format) sinh ra để làm việc này. Nó dùng chung quy tắc verbs (%s, %d...) với Printf, chỉ khác là nó return (trả về) một chuỗi.

func main() {
    orderID := 1024
    status := "PAID"
    
    // Tạo câu thông báo và lưu vào biến
    logMsg := fmt.Sprintf("Đơn hàng #%d đã chuyển sang trạng thái %s", orderID, status)
    
    // Lúc này logMsg là một chuỗi, bạn có thể gửi lên Telegram, lưu DB...
    // fmt.Println(logMsg) 
}

3. Nhập dữ liệu (Input) với fmt.Scan

Để tạo một ứng dụng CLI (Command Line Interface) có tính tương tác, chúng ta cần đọc dữ liệu người dùng nhập vào từ bàn phím. fmt cung cấp hàm Scan, ScanlnScanf.

Một sự đụng độ sớm với Con Trỏ (Pointer):

Để hàm Scan có thể thay đổi giá trị của một biến sau khi bạn nhập từ bàn phím, bạn phải truyền địa chỉ bộ nhớ của biến đó vào hàm, bằng cách đặt dấu & trước tên biến.

package main

import "fmt"

func main() {
    var name string
    var age int
    
    fmt.Print("Vui lòng nhập tên và tuổi của bạn (cách nhau bởi dấu cách): ")
    
    // Truyền địa chỉ của name (&name) và age (&age)
    fmt.Scan(&name, &age)
    
    fmt.Printf("Xác nhận: Tên bạn là %s, %d tuổi.\n", name, age)
}

Hạn chế chết người của fmt.Scan:

Hàm fmt.Scan sử dụng dấu cách (Space) hoặc dấu xuống dòng (Enter) để phân tách các biến.

Điều này có nghĩa là nếu bạn yêu cầu người dùng nhập họ tên đầy đủ, ví dụ: Nguyễn Huy Hoàng, hàm Scan sẽ tưởng "Nguyễn" là một biến, "Huy" là biến thứ hai.

Giải pháp thay thế cho việc nhập chuỗi dài:

Khi làm việc thực tế, để nhập một chuỗi có chứa dấu cách (đọc hết một dòng), các Gopher không dùng fmt.Scan, mà dùng package bufio (Buffered I/O) kết hợp với os.Stdin:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    // Khởi tạo một reader đọc dữ liệu từ Standard Input (bàn phím)
    reader := bufio.NewReader(os.Stdin)
    
    fmt.Print("Nhập họ tên đầy đủ của bạn: ")
    
    // Đọc cho đến khi gặp ký tự xuống dòng (\n)
    fullName, _ := reader.ReadString('\n')
    
    fmt.Printf("Xin chào %s", fullName)
}

Tổng kết

Tóm gọn "công lực" của fmt:

  • Output Console: Dùng Println để in nhanh, dùng Printf để định dạng chuyên nghiệp với các verbs (%s, %d, %+v).
  • String Formatting: Dùng Sprintf để "nặn" ra một chuỗi chuẩn chỉnh và lưu vào biến phục vụ cho API hay Logging.
  • Input Console: Dùng Scan cho việc đọc các biến đơn lẻ (không chứa dấu cách). Dùng bufio khi cần đọc một dòng dài (có chứa dấu cách).

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí