本文 AI 產出,尚未審核

Golang – 基本語法與資料型態

主題:格式化輸出(fmt.Printf, fmt.Sprintf


簡介

在日常開發中,將資料以易讀的方式輸出是每個程式設計師必備的技巧。Go 語言提供的 fmt 套件,不僅能夠簡單地列印字串,還支援豐富的格式化功能,讓開發者可以精確控制輸出的外觀、寬度、對齊方式以及數值的進位制等。

掌握 fmt.Printffmt.Sprintf 的用法,能讓你在除錯、日誌記錄、報表產生或是 CLI 工具的使用者介面上,寫出既美觀又易於維護的輸出。本篇文章將從概念說明、實作範例、常見陷阱到最佳實踐,全面介紹 Go 的格式化輸出。


核心概念

1. fmt 套件的兩大函式

函式 作用 主要回傳值
fmt.Printf(format, a…) 直接把格式化結果寫入標準輸出 (stdout) 無(回傳寫入的位元組數)
fmt.Sprintf(format, a…) 產生格式化字串並回傳 格式化後的 string

重點Printf 用於即時顯示;Sprintf 用於組合字串後再做其他處理(例如寫入檔案、傳遞給 API)。


2. 格式化動詞(Verb)

Go 使用 百分號 (%) 開頭的動詞來指定資料的顯示方式,常見的有:

動詞 說明 範例
%v 預設格式(結構體會列出欄位) fmt.Printf("%v", p)
%+v %v,但會顯示結構體欄位名稱 fmt.Printf("%+v", p)
%#v Go 語法表示的值(可直接用於 go run fmt.Printf("%#v", p)
%T 顯示資料的型別 fmt.Printf("%T", x)
%d 十進位整數 fmt.Printf("%d", 42)
%b 二進位 fmt.Printf("%b", 5)
%x / %X 十六進位(小寫/大寫) fmt.Printf("%x", 255)
%f 浮點數(預設 6 位小數) fmt.Printf("%f", 3.14159)
%e / %E 科學記號(小寫/大寫) fmt.Printf("%e", 1.23e4)
%s 字串 fmt.Printf("%s", "hello")
%q 帶雙引號的字串(可直接作為 Go 原始字面值) fmt.Printf("%q", "go")
%t 布林值 (true/false) fmt.Printf("%t", true)
%p 指標位址(十六進位) fmt.Printf("%p", &x)

3. 旗標(Flag)與寬度、精度

旗標 功能
- 左對齊(預設右對齊)
+ 顯示正負號(對於數值)
(空格) 正數前加空格,負數前保留負號
# 依據動詞的不同,添加前綴(如 0x0
0 前置零填充(寬度不足時)

寬度:在動詞前寫入一個整數,指定最小欄位寬度。
精度:在寬度之後加上 . 再寫入整數,對於浮點數表示小數位數,對於字串則限制最大長度。

fmt.Printf("%8d", 42)   // "      42"
fmt.Printf("%-8d", 42)  // "42      "
fmt.Printf("%08d", 42)  // "00000042"
fmt.Printf("%.2f", 3.14159) // "3.14"
fmt.Printf("%6.2f", 3.14159) // "  3.14"

程式碼範例

以下示範 5 個常見且實用的格式化情境,均附有說明註解。

範例 1:列印結構體與欄位名稱

package main

import (
	"fmt"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	p := Person{Name: "Alice", Age: 30}

	// %v 只顯示值
	fmt.Printf("預設: %v\n", p)          // {Alice 30}
	// %+v 顯示欄位名稱
	fmt.Printf("欄位名稱: %+v\n", p)      // {Name:Alice Age:30}
	// %#v 產生可直接編譯的 Go 語法
	fmt.Printf("Go 語法: %#v\n", p)      // main.Person{Name:"Alice", Age:30}
}

範例 2:對齊與填充 – 報表排版

package main

import "fmt"

func main() {
	fmt.Println("商品\t價格\t庫存")
	fmt.Printf("%-10s %8.2f %6d\n", "蘋果", 12.5, 120) // 左對齊商品名
	fmt.Printf("%-10s %08.2f %6d\n", "香蕉", 8.3, 45) // 前置零填充價格
}

輸出

商品    價格    庫存
蘋果        12.50    120
香蕉        08.30     45

範例 3:十六進位與二進位顯示 – 位元操作除錯

package main

import "fmt"

func main() {
	var flags uint8 = 0b10101010 // 170

	fmt.Printf("十進位: %d\n", flags)
	fmt.Printf("十六進位: %#x\n", flags) // 加上 0x 前綴
	fmt.Printf("二進位: %08b\n", flags)   // 8 位、前置零
}

範例 4:使用 Sprintf 組合字串 – 日誌與錯誤訊息

package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	filename := "data.txt"
	line := 42
	msg := fmt.Sprintf("檔案 %s 第 %d 行發生錯誤", filename, line)

	// 寫入日誌檔
	logger := log.New(os.Stdout, "ERROR: ", log.LstdFlags)
	logger.Println(msg)
}

範例 5:自訂格式化 – Stringer 介面

package main

import (
	"fmt"
)

// 自訂類別實作 fmt.Stringer
type Point struct {
	X, Y float64
}

// String 方法會在 %v、%s 等動詞被呼叫時自動使用
func (p Point) String() string {
	return fmt.Sprintf("(%.2f, %.2f)", p.X, p.Y)
}

func main() {
	pt := Point{X: 3.1415, Y: 2.7182}
	fmt.Printf("座標: %v\n", pt) // 會呼叫 Point.String()
}

常見陷阱與最佳實踐

陷阱 說明 建議的解決方式
忘記加 \n Printf 不會自動換行,容易導致輸出黏在一起。 使用 fmt.Println 或在格式字串最後手動加 \n
寬度與精度寫反 fmt.Printf("%2.4f", x) 會被解讀為「最小寬度 2、精度 4」,若寬度太小會被忽略。 先確定「寬度 > 精度」再寫,或直接使用 fmt.Sprintf("%6.2f", x)
%v%+v 混用 在大型結構體中直接使用 %v 可能看不到欄位名稱,除錯時不易辨識。 除錯時使用 %+v,正式輸出時根據需求選擇。
指標與 %p %p 只接受指標類型,傳入非指標會編譯錯誤。 確認變數為指標或使用 fmt.Sprintf("%#v", &v) 取得位址資訊。
過度使用 Sprintf 大量 Sprintf 會產生不必要的字串分配,影響效能。 若僅需直接輸出,使用 Printf;若需組合字串,才使用 Sprintf

最佳實踐

  1. 統一風格:在專案中約定使用的動詞與寬度(例如表格輸出全部使用 %-12s%8d),減少維護成本。
  2. 使用 log 套件:結合 Printf 的格式化能力與 log 的時間戳、等級標記,提升日誌可讀性。
  3. 避免硬編碼:若格式化需求會變動,將寬度、精度抽成變數或常數,方便未來調整。
  4. 測試輸出:對於需要精確排版的功能(如 CLI 表格),寫單元測試比對產生的字串,確保格式不會因 Go 版本變更而破壞。

實際應用場景

  1. CLI 工具

    • 使用 Printf 產生對齊的表格、選單或進度條。
    • 例:go run ./cmd/mytool --list 列出資源時,透過 % -15s %8d 排版,使使用者一眼看出欄位意義。
  2. 日誌與監控

    • 結合 log.NewSprintf,在錯誤訊息中加入變數值、時間戳與呼叫堆疊。
    • 例:log.Printf("user=%s action=%s duration=%dms", uid, act, ms)
  3. 報表產出

    • 產生 CSV、Markdown 或 HTML 表格時,先用 Sprintf 組合每一列字串,再寫入檔案。
    • 例:row := fmt.Sprintf("%s,%d,%.2f", name, qty, price)
  4. 除錯與測試

    • 在單元測試失敗時,用 %#v 輸出結構體的完整 Go 語法,方便直接貼回程式碼作為測試案例。

總結

fmt.Printffmt.Sprintf 是 Go 語言中最常使用的格式化工具。透過熟悉各種動詞、旗標、寬度與精度的組合,你可以:

  • 快速產出美觀的 CLI 輸出
  • 撰寫結構化且易於除錯的日誌
  • 在程式內部彈性組合字串(如報表、錯誤訊息)

本文從概念說明、實作範例、常見陷阱到最佳實踐,提供了完整的學習路徑。建議讀者在日常開發中刻意練習這些格式化技巧,並將團隊的格式化風格寫入程式碼規範,長期下來會大幅提升程式碼可讀性與維護效率。祝你在 Golang 的旅程中,寫出既清晰專業的輸出!