Go 常用標準庫之 fmt 介紹與基本使用

2023-10-26 06:01:07

Go 常用標準庫之 fmt 介紹與基本使用

一、介紹

fmt 是 Go 語言中的一個常用標準庫,它用於格式化輸入和輸出資料。fmt 包提供了一系列函數,可以幫助你將資料以特定的格式列印到標準輸出(通常是終端)或將資料格式化為字串以供後續處理。這個庫的名稱 "fmt" 來自於 "format",因為它主要用於格式化資料。

fmt 包的主要功能包括:

  1. 格式化輸出:fmt 包提供了函數如 Print, Printf, Println, Fprint, Fprintf, 和 Fprintln 用於將資料輸出到標準輸出或指定的 io.Writer。你可以使用這些函數將資料以不同的格式列印到螢幕上或檔案中。
  2. 格式化輸入:fmt 包也支援從輸入源(通常是標準輸入)讀取資料,並根據格式規範解析資料。這是通過 Scan, Scanf, 和 Scanln 函數實現的。這對於從使用者獲取輸入資料非常有用。
  3. 字串格式化:你可以使用 Sprintf 函數將資料格式化為字串而不是直接輸出到標準輸出,這對於構建紀錄檔訊息或其他需要格式化的字串很有用。
  4. 錯誤格式化:fmt 包也提供了 Errorf 函數,用於將格式化的錯誤訊息作為 error 型別返回,方便錯誤處理。
  5. 格式化預留位置:在格式化字串中,你可以使用預留位置來指定如何格式化資料。常見的預留位置包括 %d(整數),%f(浮點數),%s(字串)等。

二、向外輸出

標準庫 fmt 提供了多種用於輸出的函數,每個函數都有不同的用途和輸出方式。以下是一些常用的輸出相關函數:

2.1 Print 系列

  • Print:用於將文字輸出到標準輸出。它接受任意數量的引數,並將它們串聯成一個字串輸出,不會新增換行符。
  • Printf:用於格式化輸出到標準輸出。它接受一個格式化字串和一系列引數,根據格式化字串的預留位置將引數格式化並輸出。
  • Println:類似於 Print,但會在輸出後自動新增一個換行符
func Print(a ...interface{}) (n int, err error)
func Printf(format string, a ...interface{}) (n int, err error)
func Println(a ...interface{}) (n int, err error)

舉個簡單的例子:

func main() {
	fmt.Print("Hello, ", "world")
	name := "Alice"
	age := 30
	fmt.Printf("Hello, %s. You are %d years old.\n", name, age)
	fmt.Println("Hello, world")
}

2.2 Fprint 系列

Fprint 系列函數用於將文字輸出到指定的 io.Writer 介面,而不僅僅是標準輸出。你可以將文字輸出到檔案、網路連線等。這些函數的參數列包括一個 io.Writer 引數,以及任意數量的引數。

  • Fprint:將文字輸出到指定的 io.Writer
  • Fprintf:將格式化文字輸出到指定的 io.Writer
  • Fprintln:將帶有換行符的文字輸出到指定的 io.Writer
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)

舉個例子:

func main() {
	// 向標準輸出寫入內容
	fmt.Fprintln(os.Stdout, "向標準輸出寫入內容")
	fileObj, err := os.OpenFile("./output.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		fmt.Println("開啟檔案出錯,err:", err)
		return
	}
	name := "jarvis"
	// 向開啟的檔案控制程式碼中寫入內容
	fmt.Fprintf(fileObj, "往檔案中寫如資訊:%s", name)
}

這個範例建立了一個名為 "output.txt" 的檔案,並將資料寫入檔案中。

2.3 Sprint 系列

Sprint 系列函數用於將文字輸出到字串中,而不是標準輸出或檔案。它們將文字格式化為字串並返回結果。

  • Sprint:將文字輸出到字串。
  • Sprintf:將格式化文字輸出到字串。
  • Sprintln:將帶有換行符的文字輸出到字串。
func Sprint(a ...interface{}) string
func Sprintf(format string, a ...interface{}) string
func Sprintln(a ...interface{}) string

簡單的範例程式碼如下:

func main() {
	s1 := fmt.Sprint("jarvis")
	name := "jarvis"
	age := 18
	s2 := fmt.Sprintf("name:%s,age:%d", name, age)
	s3 := fmt.Sprintln("jarvis")
	fmt.Println(s1, s2, s3)
}

2.4 Errorf 系列

Errorf 系列函數用於建立格式化的錯誤訊息並返回一個 error 型別的值。這允許你將格式化的錯誤訊息返回給呼叫者,以便更好地進行錯誤處理。這些函數的用法類似於 Sprintf,但它們返回一個 error 值而不是字串。

  • Errorf:根據format引數生成格式化字串並返回一個包含該字串的錯誤。
func Errorf(format string, a ...interface{}) error

通常使用這種方式來自定義錯誤型別,例如:

err := fmt.Errorf("這是一個錯誤")

三、格式化預留位置

*printf系列函數都支援format格式化引數,在這裡我們按照預留位置將被替換的變數型別劃分,方便查詢和記憶。

3.1 通用預留位置

通用預留位置用於格式化不同型別的資料:

預留位置 說明
%v 值的預設格式表示
%+v 類似%v,但輸出結構體時會新增欄位名
%#v 值的Go語法表示
%T 列印值的型別
%% 百分號

程式碼範例:

func main() {
	fmt.Printf("%v\n", 100)
	fmt.Printf("%v\n", false)
	o := struct{ name string }{"jarvis"}
	fmt.Printf("%v\n", o)
	fmt.Printf("%#v\n", o)
	fmt.Printf("%T\n", o)
	fmt.Printf("100%%\n")
}

3.2 布林型

預留位置 說明
%t true或false

3.3 整型

預留位置 說明
%b 表示為二進位制
%c 該值對應的unicode碼值
%d 表示為十進位制
%o 表示為八進位制
%x 表示為十六進位制,使用a-f
%X 表示為十六進位制,使用A-F
%U 表示為Unicode格式:U+1234,等價於"U+%04X"
%q 該值對應的單引號括起來的go語法字元字面值,必要時會採用安全的跳脫表示

範例程式碼如下:

n := 65
fmt.Printf("%b\n", n)
fmt.Printf("%c\n", n)
fmt.Printf("%d\n", n)
fmt.Printf("%o\n", n)
fmt.Printf("%x\n", n)
fmt.Printf("%X\n", n)

3.4 浮點數與複數

預留位置 說明
%b 無小數部分、二進位制指數的科學計數法,如-123456p-78
%e 科學計數法,如-1234.456e+78
%E 科學計數法,如-1234.456E+78
%f 有小數部分但無指數部分,如123.456
%F 等價於%f
%g 根據實際情況採用%e或%f格式(以獲得更簡潔、準確的輸出)
%G 根據實際情況採用%E或%F格式(以獲得更簡潔、準確的輸出)

範例程式碼如下:

f := 12.34
fmt.Printf("%b\n", f)
fmt.Printf("%e\n", f)
fmt.Printf("%E\n", f)
fmt.Printf("%f\n", f)
fmt.Printf("%g\n", f)
fmt.Printf("%G\n", f)

3.5 字串和[]byte

預留位置 說明
%s 直接輸出字串或者[]byte
%q 該值對應的雙引號括起來的go語法字串字面值,必要時會採用安全的跳脫表示
%x 每個位元組用兩字元十六進位制數表示(使用a-f
%X 每個位元組用兩字元十六進位制數表示(使用A-F)

範例程式碼如下:

    s := "jarvis"
    fmt.Printf("%s\n", s)
    fmt.Printf("%q\n", s)
    fmt.Printf("%x\n", s)
    fmt.Printf("%X\n", s)

3.6 指標

預留位置 說明
%p 表示為十六進位制,並加上前導的0x

範例程式碼如下:

a := 18
fmt.Printf("%p\n", &a)
fmt.Printf("%#p\n", &a)

3.7 寬度識別符號

寬度通過一個緊跟在百分號後面的十進位制數指定,如果未指定寬度,則表示值時除必需之外不作填充。精度通過(可選的)寬度後跟點號後跟的十進位制數指定。如果未指定精度,會使用預設精度;如果點號後沒有跟數位,表示精度為0。舉例如下

預留位置 說明
%f 預設寬度,預設精度
%9f 寬度9,預設精度
%.2f 預設寬度,精度2
%9.2f 寬度9,精度2
%9.f 寬度9,精度0

範例程式碼如下:

n := 88.88
fmt.Printf("%f\n", n)
fmt.Printf("%9f\n", n)
fmt.Printf("%.2f\n", n)
fmt.Printf("%9.2f\n", n)
fmt.Printf("%9.f\n", n)

3.8 其他flag

預留位置 說明
‘+’ 總是輸出數值的正負號;對%q(%+q)會生成全部是ASCII字元的輸出(通過跳脫);
’ ' 對數值,正數前加空格而負數前加負號;對字串採用%x或%X時(% x或% X)會給各列印的位元組之間加空格
‘-’ 在輸出右邊填充空白而不是預設的左邊(即從預設的右對齊切換為左對齊);
‘#’ 八進位制數前加0(%#o),十六進位制數前加0x(%#x)或0X(%#X),指標去掉前面的0x(%#p)對%q(%#q),對%U(%#U)會輸出空格和單引號括起來的go字面值;
‘0’ 使用0而不是空格填充,對於數值型別會把填充的0放在正負號後面;

舉個例子:

s := "jarvis"
fmt.Printf("%s\n", s)
fmt.Printf("%5s\n", s)
fmt.Printf("%-5s\n", s)
fmt.Printf("%5.7s\n", s)
fmt.Printf("%-5.7s\n", s)
fmt.Printf("%5.2s\n", s)
fmt.Printf("%05s\n", s)

四、獲取輸入

Go 語言的 fmt 包提供了 fmt.Scanfmt.Scanffmt.Scanln 這三個函數,用於從標準輸入獲取使用者的輸入。這些函數允許你與使用者互動,從標準輸入流中讀取不同型別的資料並將其儲存在相應的變數中。

4.1 fmt.Scan 函數

Scan 函數用於從標準輸入中獲取使用者的輸入,並將輸入的資料儲存在變數中。它根據空格分隔輸入,適合獲取多個輸入值。

函數定義如下:

func Scan(a ...interface{}) (n int, err error)
  • Scan從標準輸入掃描文字,讀取由空白符分隔的值儲存到傳遞給本函數的引數中,換行符視為空白符。
  • 本函數返回成功掃描的資料個數和遇到的任何錯誤。如果讀取的資料個數比提供的引數少,會返回一個錯誤報告原因。

具體程式碼範例如下:

package main

import "fmt"

func main() {
    var name string
    var age int

    fmt.Print("Enter your name: ")
    fmt.Scan(&name)
    fmt.Print("Enter your age: ")
    fmt.Scan(&age)

    fmt.Printf("Name: %s, Age: %d\n", name, age)
}

在這個範例中,fmt.Scanf 使用格式字串 %s %d 來解析輸入的姓名和年齡。

4.2 fmt.Scanln 函數

Scanln 函數用於從標準輸入中獲取使用者的輸入,並將輸入的資料儲存在變數中,每行一個變數。它通常用於獲取多個輸入值,每個值在單獨的行中輸入。

函數定義如下:

func Scanln(a ...interface{}) (n int, err error)
  • Scanln類似Scan,它在遇到換行時才停止掃描。最後一個資料後面必須有換行或者到達結束位置。
  • 本函數返回成功掃描的資料個數和遇到的任何錯誤。

程式碼範例:

package main

import "fmt"

func main() {
    var name string
    var age int

    fmt.Print("Enter your name: ")
    fmt.Scanln(&name)
    fmt.Print("Enter your age: ")
    fmt.Scanln(&age)

    fmt.Printf("Name: %s, Age: %d\n", name, age)
}

在上面的範例中,fmt.Scanln 用於獲取使用者輸入的姓名和年齡,並將它們儲存在相應的變數中。輸入的每一行都對應一個變數。

4.3 fmt.Scanf 函數

Scanf 函數用於根據格式規範解析輸入,並將資料儲存在變數中。它允許你指定輸入的格式,並可以處理不同型別的資料。

函數簽名如下:

func Scanf(format string, a ...interface{}) (n int, err error)
  • Scanf從標準輸入掃描文字,根據format引數指定的格式去讀取由空白符分隔的值儲存到傳遞給本函數的引數中。
  • 本函數返回成功掃描的資料個數和遇到的任何錯誤。

程式碼範例如下:

package main

import "fmt"

func main() {
    var name string
    var age int

    fmt.Print("Enter your name and age: ")
    fmt.Scanf("%s %d", &name, &age)

    fmt.Printf("Name: %s, Age: %d\n", name, age)
}

在這個範例中,fmt.Scanf 使用格式字串 %s %d 來解析輸入的姓名和年齡。

4.4 使用 bufio 包獲取輸入

bufio 包提供了一種更靈活的方式來處理輸入,特別是在需要完整讀取一行或多行輸入的情況下。你可以使用 bufio.NewReader 建立一個輸入緩衝區,然後使用 ReadString 函數來讀取輸入,直到指定的分隔符(例如換行符 \n)。這允許你獲取包含空格在內的完整輸入內容。

func bufioDemo() {
    reader := bufio.NewReader(os.Stdin) // 從標準輸入生成讀物件
    fmt.Print("請輸入內容:")
    text, _ := reader.ReadString('\n') // 讀取直到換行符
    text = strings.TrimSpace(text)
    fmt.Printf("%#v\n", text)
}

4.5 使用 Fscan 系列函數

Fscan 系列函數允許你從 io.Reader 介面中讀取資料,而不僅僅是標準輸入。這些函數與 fmt.Scanfmt.Scanffmt.Scanln 類似,但允許你從任何實現 io.Reader 介面的地方讀取資料。

  • Fscan:從 io.Reader 中讀取資料。
  • Fscanln:從 io.Reader 中讀取一行資料。
  • Fscanf:根據指定的格式從 io.Reader 中讀取資料。
func Fscan(r io.Reader, a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)

程式碼範例:

package main

import (
    "fmt"
    "strings"
)

func main() {
    input := "42 John"
    reader := strings.NewReader(input) // 從字串生成讀物件

    var age int
    var name string

    n, err := fmt.Fscanf(reader, "%d %s", &age, &name)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Printf("Read %d values: Age: %d, Name: %s\n", n, age, name)
}

4.6 使用 Sscan 系列函數

Sscan 系列函數允許你從字串中讀取資料,而不僅僅是從標準輸入。這些函數與 fmt.Scanfmt.Scanffmt.Scanln 類似,但允許你從字串中讀取資料。

  • Sscan:從字串中讀取資料。
  • Sscanln:從字串中讀取一行資料。
  • Sscanf:根據指定的格式從字串中讀取資料。
func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)

程式碼範例:

package main

import (
    "fmt"
)

func main() {
    input := "Alice 30"
    var name string
    var age int

    n, err := fmt.Sscanf(input, "%s %d", &name, &age)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Printf("Read %d values: Name: %s, Age: %d\n", n, name, age)
}