fmt包實現了相似C語言printf和scanf的格式化I/O。格式化動做('verb')源自C語言但更簡單。本文將介紹 Stringer, GoStringer, State, Formatter這幾個接口的做用。python
type Stringer interface {
String() string
}
複製代碼
若是一個對象實現了這個Stringer
接口,那麼經過%s
,%v
打印,這時候會判斷這個對象是否實現了Stringer
接口,若是實現了,就調用對象的String
方法,怎麼感受有點像python中的 __str__
方法。json
package main
import "fmt"
type person struct {
Name string
age int
}
func (p *person) String() string {
return p.Name
}
func main() {
p := &person{"jack",22}
println(p) // 0xc000090000,這個打印的是地址
fmt.Println(p) // jack
fmt.Printf("%v\n",p) // jack
fmt.Printf("%s\n",p) // jack
}
複製代碼
對於內建函數
println()
,print()
是不能調用到String
方法的。函數
對於某些結構體,當某些字段不但願在打印的時候輸出時,能夠實現
Stringer
接口,達到隱藏某些字段的目的。學習
type GoStringer interface {
GoString() string
}
複製代碼
實現了GoStringer
接口的對象,當採用 %#v
格式化輸出時(其餘的形式會不會呢?),會調用GoString方法來生成輸出定義了該類型值的go語法表示。就是說這個是go特有的,好比&main.person{Name:"jack", age:22}
這種樣式的就是go語法來表示一個對象,固然咱們能夠本身定義。ui
package main
import (
"encoding/json"
"fmt"
)
type person struct {
Name string
age int
}
func (p *person) GoString() string {
b, _ :=json.Marshal(p)
return string(b)
}
func main() {
p := &person{"jack",22}
fmt.Printf("%v\n",p) // &{jack 22}
fmt.Printf("%#v\n",p) // {"Name":"jack"} , GoString的輸出
fmt.Printf("%+v\n",p) // &{Name:jack age:22}
}
複製代碼
這裏咱們經過實現
GoStringer
接口,並返回對象 json 序列化後的字符串來達到改寫對象的go語法表示。spa
type State interface {
// Write方法用來寫入格式化的文本
Write(b []byte) (ret int, err error)
// Width返回寬度值,及其是否被設置
Width() (wid int, ok bool)
// Precision返回精度值,及其是否被設置
Precision() (prec int, ok bool)
// Flag報告是否設置了flag c(一個字符,如+、-、#等)
Flag(c int) bool
}
複製代碼
在學習 State
接口以前,咱們先來看一下,go的格式化語法 code
%
表示格式化的開始位置orm
%
後面緊跟的+
號,表示flags
cdn
+
後面的3.2
表示寬度Width
和精度Precision
對象
f
爲佔位符verb
那麼go格式化語法包括了 開始位置
,flags
,寬度
,精度
,佔位符
五個部分組成,State
接口包含了其中三項信息。在解析格式化字符串的時候,會一個一個解析,當解析完成一個這樣的%+3.2f
總體結構之後,數據保存到State
,下一步調用printArg
進行格式化,並將格式化後的數據寫入buffer
,全部的結構解析完成以後寫入到io.Writer
,並在控制檯輸出(後面會具體討論代碼是怎麼實現的)。
type Formatter interface {
Format(f State, c rune)
}
複製代碼
Formatter
用於實現自定義格式化方法。可經過在自定義結構體中實現 Format
方法來實現這個目的。標準庫中使用在 /usr/local/go/src/fmt/print.go:580
,會首先判斷結構體是否實現了 Formatter
接口,實現了則利用自定義 Formatter 格式化參數。未實現,則最大程度的利用 go語法格式 GoStringer
默認規則去格式化參數,其次是 error
和 Stringer
接口。
咱們來看一下大廠 B站是怎麼實現這個接口的
// Format is a support routine for fmt.Formatter. It accepts the decimal
// formats 'd' and 'f', and handles both equivalently.
// Width, precision, flags and bases 2, 8, 16 are not supported.
func (x *Dec) Format(s fmt.State, ch rune) {
if ch != 'd' && ch != 'f' && ch != 'v' && ch != 's' {
fmt.Fprintf(s, "%%!%c(dec.Dec=%s)", ch, x.String())
return
}
fmt.Fprintf(s, x.String())
}
複製代碼
是否是很簡單? 😝
本小節介紹了fmt包中的幾個基礎接口和使用。下一篇,咱們將深刻fmt包的實現。