// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // go/src/fmt/print.go // version 1.7 // 格式化輸入輸出的用法請參考:http://www.cnblogs.com/golove/p/3284304.html package fmt import ( "errors" "io" "os" "reflect" "sync" "unicode/utf8" ) // 用於 buffer.WriteString 的字符串,比使用 buffer.Write 寫入字節數組更節省開銷。 const ( commaSpaceString = ", " nilAngleString = "<nil>" nilParenString = "(nil)" nilString = "nil" mapString = "map[" percentBangString = "%!" missingString = "(MISSING)" badIndexString = "(BADINDEX)" panicString = "(PANIC=" extraString = "%!(EXTRA " badWidthString = "%!(BADWIDTH)" badPrecString = "%!(BADPREC)" noVerbString = "%!(NOVERB)" invReflectString = "<invalid reflect.Value>" ) // State 提供格式化器的相關信息,傳遞給 Formatter 使用 type State interface { // 將格式化後的數據寫入緩衝區中準備打印 Write(b []byte) (n int, err error) // 返回寬度信息及其是否被設置 Width() (wid int, ok bool) // 返回精度信息及其是否被設置 Precision() (prec int, ok bool) // 返回旗標 [+- 0#] 是否被設置 Flag(c int) bool } // Formatter 用於讓自定義類型實現本身的格式化過程。 // Format 方法會被格式化器調用,只要對應的 arg 實現了該方法。 // f 是格式化器的當前狀態,c 是佔位符中指定的動詞。 // 能夠經過 f.Width 和 f.Precision 獲取佔位符中的寬度和精度信息 // 能夠經過 f.Flag(c) 判斷佔位符中是否設置了某個旗標(+- 0#) // 格式化後的結果經過 f.Write 寫回去。 type Formatter interface { Format(f State, c rune) } // 格式化器在格式化某值的本地字符串時,會調用其類型的 String 方法。 type Stringer interface { String() string } // 格式化器在格式化某值的 Go 語法字符串(%#v)時,會調用其類型的 GoString 方法。 type GoStringer interface { GoString() string } // 使用簡單的 []byte 代替 bytes.Buffer 避免大的依賴。 type buffer []byte func (b *buffer) Write(p []byte) { *b = append(*b, p...) } func (b *buffer) WriteString(s string) { *b = append(*b, s...) } func (b *buffer) WriteByte(c byte) { *b = append(*b, c) } func (bp *buffer) WriteRune(r rune) { // 單字節 UTF8 字符快速處理 if r < utf8.RuneSelf { *bp = append(*bp, byte(r)) return } b := *bp // 準備緩衝區空間,用於寫入 rune 編碼 n := len(b) for n+utf8.UTFMax > cap(b) { b = append(b, 0) } // 寫入 rune 字符(必須提供足夠的空間) w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r) // 截斷無效部分 *bp = b[:n+w] } // pp 是格式化器,整個格式化過程都是由它完成的。 // 格式化結果將寫入緩衝區 buf 中。 type pp struct { // 存放結果的緩衝區 []byte buf buffer // 當前須要格式化的參數 arg interface{} // 用於代替 arg,有反射值的時候就不用 arg 了 value reflect.Value // fmt 用於格式化基礎類型 // fmt 在 format.go 中定義 fmt fmt // 是否使用了自定義的 argNum(argNum 是正在處理的 arg 的序號)。 reordered bool // 當前 argNum 是否合法(好比自定義 argNum 格式錯誤,或 argNum 超出範圍)。 goodArgNum bool // 用在 catchPanic 方法中,避免無限的「恐慌->恢復->恐慌->恢復...」遞歸狀況。 panicking bool // 當打印一個錯誤「動詞」時設置 erroring,以防止調用 handleMethods。 // 錯誤的「動詞」不能用自定義方法去處理。 erroring bool } // 臨時對象池 var ppFree = sync.Pool{ New: func() interface{} { return new(pp) }, } // 分配一個新的格式化器,或從臨時對象池中取出一個。 func newPrinter() *pp { p := ppFree.Get().(*pp) p.panicking = false p.erroring = false p.fmt.init(&p.buf) return p } // 將用過的格式化器存入臨時對象池中,避免每次調用時都從新分配內存。 func (p *pp) free() { p.buf = p.buf[:0] p.arg = nil p.value = reflect.Value{} ppFree.Put(p) } // 實現 State 接口 func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent } // 實現 State 接口 func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent } // 實現 State 接口 func (p *pp) Flag(b int) bool { switch b { case '-': return p.fmt.minus case '+': return p.fmt.plus || p.fmt.plusV case '#': return p.fmt.sharp || p.fmt.sharpV case ' ': return p.fmt.space case '0': return p.fmt.zero } return false } // 實現 State 接口 func (p *pp) Write(b []byte) (ret int, err error) { p.buf.Write(b) return len(b), nil } // 這些函數以 f 結尾,並須要一個格式字符串。 // Fprintf 根據格式字符串 format 對 a 中提供的 arg 進行格式化, // 並將結果寫入 w。返回寫入的字節數和錯誤信息。 func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { p := newPrinter() p.doPrintf(format, a) n, err = w.Write(p.buf) p.free() return } // Printf 根據格式字符串 format 對 a 中提供的 arg 進行格式化, // 並將結果寫入標準輸出。返回寫入的字節數和錯誤信息。 func Printf(format string, a ...interface{}) (n int, err error) { return Fprintf(os.Stdout, format, a...) } // Sprintf 根據格式字符串 format 對 a 中提供的 arg 進行格式化,並返回結果。 func Sprintf(format string, a ...interface{}) string { p := newPrinter() p.doPrintf(format, a) s := string(p.buf) p.free() return s } // Errorf 根據格式字符串 format 對 a 中提供的 arg 進行格式化, // 並將結果包裝成 error 類型返回。 func Errorf(format string, a ...interface{}) error { return errors.New(Sprintf(format, a...)) } // 這些函數沒有格式字符串 // Fprint 使用默認格式對 a 中提供的 arg 進行格式化,並將結果寫入 w。 // 返回寫入的字節數和錯誤信息。 // 非字符串 arg 之間會添加空格。 func Fprint(w io.Writer, a ...interface{}) (n int, err error) { p := newPrinter() p.doPrint(a) n, err = w.Write(p.buf) p.free() return } // Print 使用默認格式對 a 中提供的 arg 進行格式化,並將結果寫入到標準輸出。 // 返回寫入的字節數和錯誤信息。 // 非字符串 arg 之間會添加空格。 func Print(a ...interface{}) (n int, err error) { return Fprint(os.Stdout, a...) } // Sprint 使用默認格式對 a 中提供的 arg 進行格式化,並返回結果。 // 非字符串 arg 之間會添加空格。 func Sprint(a ...interface{}) string { p := newPrinter() p.doPrint(a) s := string(p.buf) p.free() return s } // 這些函數以 ln 結尾,不須要格式字符串。 // Fprintln 使用默認格式對 a 中提供的 arg 進行格式化,並將結果寫入 w。 // 返回寫入的字節數和錯誤信息。 // 各 arg 之間會添加空格,並在最後添加換行符。 func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { p := newPrinter() p.doPrintln(a) n, err = w.Write(p.buf) p.free() return } // Println 使用默認格式對 a 中提供的 arg 進行格式化,並將結果寫入標準輸出。 // 返回寫入的字節數和錯誤信息。 // 各 arg 之間會添加空格,並在最後添加換行符。 func Println(a ...interface{}) (n int, err error) { return Fprintln(os.Stdout, a...) } // Sprintln 使用默認格式對 a 中提供的 arg 進行格式化,並返回結果。 // 各 arg 之間會添加空格,並在最後添加換行符。 func Sprintln(a ...interface{}) string { p := newPrinter() p.doPrintln(a) s := string(p.buf) p.free() return s } // 獲取結構體的第 i 個字段。若是字段自身是接口類型, // 則返回接口中的值,而不是接口自身。 func getField(v reflect.Value, i int) reflect.Value { val := v.Field(i) if val.Kind() == reflect.Interface && !val.IsNil() { val = val.Elem() } return val } // 判斷整數值是否過大,超出格式化寬度和精度的容許範圍。 func tooLarge(x int) bool { const max int = 1e6 return x > max || x < -max } // 將字符串格式的數值儘量解析爲 int 型數值,直到遇到非數字字符爲止。 // 若是沒有解析出任何數值,則 num 返回 0,且 isnum 返回 false。 // newi 返回待處理下標(即:處理完開頭的全部數字以後的下一個字符的下標) func parsenum(s string, start, end int) (num int, isnum bool, newi int) { if start >= end { return 0, false, end } // 在「數字字符串」範圍內循環,並計算結果 for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ { if tooLarge(num) { return 0, false, end } num = num*10 + int(s[newi]-'0') isnum = true } return } // 寫入未知類型的值 func (p *pp) unknownType(v reflect.Value) { if !v.IsValid() { p.buf.WriteString(nilAngleString) return } p.buf.WriteByte('?') p.buf.WriteString(v.Type().String()) p.buf.WriteByte('?') } // 處理無效動詞(沒法被指定類型識別的動詞)。好比布爾型只能 // 識別 v 和 t 兩個動詞,那麼其它動詞對布爾型來講就是無效動詞。 func (p *pp) badVerb(verb rune) { // 標記 erroring,防止調用 handleMethods 方法, // 由於無效動詞不能交給類型本身去處理。 p.erroring = true p.buf.WriteString(percentBangString) // "%!" p.buf.WriteRune(verb) p.buf.WriteByte('(') switch { case p.arg != nil: p.buf.WriteString(reflect.TypeOf(p.arg).String()) p.buf.WriteByte('=') p.printArg(p.arg, 'v') case p.value.IsValid(): // (*int)(nil) p.buf.WriteString(p.value.Type().String()) p.buf.WriteByte('=') p.printValue(p.value, 'v', 0) default: p.buf.WriteString(nilAngleString) // "<nil>" } p.buf.WriteByte(')') p.erroring = false } // 寫入布爾型值 func (p *pp) fmtBool(v bool, verb rune) { switch verb { case 't', 'v': p.fmt.fmt_boolean(v) default: p.badVerb(verb) } } // 寫入整型值的十六進制格式 // leading0x:是否添加 0x 前導符 func (p *pp) fmt0x64(v uint64, leading0x bool) { // 經過臨時設置 # 旗標來添加 0x 前導符 sharp := p.fmt.sharp p.fmt.sharp = leading0x p.fmt.fmt_integer(v, 16, unsigned, ldigits) p.fmt.sharp = sharp } // 寫入整型值,包有符號和無符號 func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) { switch verb { case 'v': // 十進制 if p.fmt.sharpV && !isSigned { // Go 語法格式:無符號型用 0xFF 格式 p.fmt0x64(v, true) } else { p.fmt.fmt_integer(v, 10, isSigned, ldigits) } case 'd': // 十進制 p.fmt.fmt_integer(v, 10, isSigned, ldigits) case 'b': // 二進制 p.fmt.fmt_integer(v, 2, isSigned, ldigits) case 'o': // 八進制 p.fmt.fmt_integer(v, 8, isSigned, ldigits) case 'x': // 十六進制(小寫) p.fmt.fmt_integer(v, 16, isSigned, ldigits) case 'X': // 十六進制(大寫) p.fmt.fmt_integer(v, 16, isSigned, udigits) case 'c': // 字符 p.fmt.fmt_c(v) case 'q': // '字符' if v <= utf8.MaxRune { p.fmt.fmt_qc(v) } else { p.badVerb(verb) } case 'U': // U+FFFF p.fmt.fmt_unicode(v) default: p.badVerb(verb) } } // 寫入浮點型值 func (p *pp) fmtFloat(v float64, size int, verb rune) { // fmt_float 的最後一個參數指定精度 switch verb { case 'v': p.fmt.fmt_float(v, size, 'g', -1) case 'b', 'g', 'G': p.fmt.fmt_float(v, size, verb, -1) case 'f', 'e', 'E': p.fmt.fmt_float(v, size, verb, 6) case 'F': p.fmt.fmt_float(v, size, 'f', 6) default: p.badVerb(verb) } } // 寫入複數型值 // 將實部和虛部用 fmtFloat 分別格式化後寫入緩衝區。 func (p *pp) fmtComplex(v complex128, size int, verb rune) { switch verb { case 'v', 'b', 'g', 'G', 'f', 'F', 'e', 'E': oldPlus := p.fmt.plus p.buf.WriteByte('(') p.fmtFloat(real(v), size/2, verb) // 虛部總有一個符號 p.fmt.plus = true p.fmtFloat(imag(v), size/2, verb) p.buf.WriteString("i)") p.fmt.plus = oldPlus default: p.badVerb(verb) } } // 寫入字符串 func (p *pp) fmtString(v string, verb rune) { switch verb { case 'v': // 字符串(無引號) if p.fmt.sharpV { // Go 語法格式:字符串(有引號) p.fmt.fmt_q(v) } else { p.fmt.fmt_s(v) } case 's': // 字符串(無引號) p.fmt.fmt_s(v) case 'x': // 十六進制字符串(小寫) p.fmt.fmt_sx(v, ldigits) case 'X': // 十六進制字符串(大寫) p.fmt.fmt_sx(v, udigits) case 'q': // 字符串(有引號) p.fmt.fmt_q(v) default: p.badVerb(verb) } } // 寫入字節切片型 func (p *pp) fmtBytes(v []byte, verb rune, typeString string) { switch verb { case 'v', 'd': // [元素 元素 ...] if p.fmt.sharpV { // Go 語法格式:[]byte{0xFF, 0xFF, ...} p.buf.WriteString(typeString) if v == nil { p.buf.WriteString(nilParenString) // "(nil)" return } p.buf.WriteByte('{') for i, c := range v { if i > 0 { // 第一個元素前面不添加分隔符 p.buf.WriteString(commaSpaceString) // ", " } p.fmt0x64(uint64(c), true) } p.buf.WriteByte('}') } else { p.buf.WriteByte('[') for i, c := range v { if i > 0 { // 第一個元素前面不添加分隔符 p.buf.WriteByte(' ') } p.fmt.fmt_integer(uint64(c), 10, unsigned, ldigits) } p.buf.WriteByte(']') } case 's': // 字符串(無引號) p.fmt.fmt_s(string(v)) case 'x': // 十六進制字符串(小寫) p.fmt.fmt_bx(v, ldigits) case 'X': // 十六進制字符串(大寫) p.fmt.fmt_bx(v, udigits) case 'q': // 字符串(有引號) p.fmt.fmt_q(string(v)) default: p.printValue(reflect.ValueOf(v), verb, 0) } } // 寫入指針 func (p *pp) fmtPointer(value reflect.Value, verb rune) { var u uintptr // 只接受指定類型的值並轉換爲指針:通道、函數、映射、指針、切片、非安全指針 switch value.Kind() { case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: u = value.Pointer() default: p.badVerb(verb) return } switch verb { case 'v': // 0xffffffffff(或 "nil") if p.fmt.sharpV { // Go 語法格式:(類型)(0xffffffffff) p.buf.WriteByte('(') p.buf.WriteString(value.Type().String()) p.buf.WriteString(")(") if u == 0 { p.buf.WriteString(nilString) // "nil" } else { p.fmt0x64(uint64(u), true) } p.buf.WriteByte(')') } else { if u == 0 { p.fmt.padString(nilAngleString) // <"nil"> } else { p.fmt0x64(uint64(u), !p.fmt.sharp) } } case 'p': // 0xffffffffff p.fmt0x64(uint64(u), !p.fmt.sharp) case 'b', 'o', 'd', 'x', 'X': // 當整數處理 p.fmtInteger(uint64(u), unsigned, verb) default: p.badVerb(verb) } } // 用在 handleMethods 方法中,捕捉自定義類型的格式化方法中產生的恐慌。 func (p *pp) catchPanic(arg interface{}, verb rune) { if err := recover(); err != nil { // 若是 arg 是一個 nil,只須要寫入 "<nil>" 便可。出現這種 panic 最有 // 可能的緣由是 Stringer 未能防止無效的 nil 或以 nil 作爲接收器。不管 // 哪一種狀況,寫入 "<nil>" 是一個很好的選擇。 if v := reflect.ValueOf(arg); v.Kind() == reflect.Ptr && v.IsNil() { p.buf.WriteString(nilAngleString) return } // 不然經過後面的一系列語句打印一個簡明的 panic 信息便可。 if p.panicking { // 若是後面的 printArg 中又產生了 panic,那麼真的就恐慌了。 panic(err) } p.fmt.clearflags() // 一個佔位符算處理完了,進行復位。 p.buf.WriteString(percentBangString) p.buf.WriteRune(verb) p.buf.WriteString(panicString) p.panicking = true // 防止「恐慌 -> 恢復 -> 恐慌 -> 恢復」無限遞歸 p.printArg(err, 'v') p.panicking = false p.buf.WriteByte(')') } } // 判斷 p.arg 是否有自定義的格式化方法,若是有就調用,若是沒有,就返回 false。 func (p *pp) handleMethods(verb rune) (handled bool) { // 若是正在處理無效動詞,則不使用自定義方法。 if p.erroring { return } // 實現了 Format 方法 if formatter, ok := p.arg.(Formatter); ok { // 標記 p.arg 已經處理了。 handled = true // 捕捉 Format() 中產生的恐慌 defer p.catchPanic(p.arg, verb) // 處理 arg formatter.Format(p, verb) return } // 要求 Go 語法格式 if p.fmt.sharpV { // 實現了 GoString 方法 if stringer, ok := p.arg.(GoStringer); ok { handled = true // 捕捉 GoString() 中產生的恐慌 defer p.catchPanic(p.arg, verb) // 處理 arg p.fmt.fmt_s(stringer.GoString()) return } } else { // 若是指定了字符串相關的動詞,則判斷 p.arg 能否轉換爲字符串 switch verb { case 'v', 's', 'x', 'X', 'q': switch v := p.arg.(type) { case error: // Error() 方法能夠獲取到字符串 handled = true // 捕捉 Error() 中產生的恐慌 defer p.catchPanic(p.arg, verb) // 處理 arg p.fmtString(v.Error(), verb) return case Stringer: // String() 方法能夠獲取到字符串 handled = true // 捕捉 String() 中產生的恐慌 defer p.catchPanic(p.arg, verb) // 處理 arg p.fmtString(v.String(), verb) return } } } return false } // printArg 只處理非嵌套的基礎類型,其它類型的 arg,則交給 printValue 進行 // 反射處理。若是 arg 有自定義的格式化方法,則 printArg 會調用自定義方法進行處理。 // 對於嵌套中的類型,也是交給 printValue 進行處理。 func (p *pp) printArg(arg interface{}, verb rune) { // 記下當前 arg 內容,方便其它地方使用 p.arg = arg // 初始化爲零值,防止其它地方調用了 nil p.value = reflect.Value{} if arg == nil { switch verb { case 'T', 'v': p.fmt.padString(nilAngleString) // "<nil>" default: p.badVerb(verb) } return } // %T(類型)和 %p(指針)是特殊動詞,須要優先處理。 switch verb { case 'T': p.fmt.fmt_s(reflect.TypeOf(arg).String()) return case 'p': p.fmtPointer(reflect.ValueOf(arg), 'p') return } // 簡單類型能夠不用反射處理,直接調用相應的處理函數便可。 switch f := arg.(type) { case bool: p.fmtBool(f, verb) case float32: p.fmtFloat(float64(f), 32, verb) case float64: p.fmtFloat(f, 64, verb) case complex64: p.fmtComplex(complex128(f), 64, verb) case complex128: p.fmtComplex(f, 128, verb) case int: p.fmtInteger(uint64(f), signed, verb) case int8: p.fmtInteger(uint64(f), signed, verb) case int16: p.fmtInteger(uint64(f), signed, verb) case int32: p.fmtInteger(uint64(f), signed, verb) case int64: p.fmtInteger(uint64(f), signed, verb) case uint: p.fmtInteger(uint64(f), unsigned, verb) case uint8: p.fmtInteger(uint64(f), unsigned, verb) case uint16: p.fmtInteger(uint64(f), unsigned, verb) case uint32: p.fmtInteger(uint64(f), unsigned, verb) case uint64: p.fmtInteger(f, unsigned, verb) case uintptr: p.fmtInteger(uint64(f), unsigned, verb) case string: p.fmtString(f, verb) case []byte: p.fmtBytes(f, verb, "[]byte") case reflect.Value: p.printValue(f, verb, 0) default: // 其它類型可能有自定義的格式化方法,在這裏調用。 if !p.handleMethods(verb) { // 若是該類型沒有可用於格式化的方法,則經過反射處理。 p.printValue(reflect.ValueOf(f), verb, 0) } } } var byteType = reflect.TypeOf(byte(0)) // printValue 相似於 printArg 可是以反射值做爲參數,而不是接口值。 // 它不處理 'p' 和 'T' 動詞,由於這些已經被 printArg 處理過了。 // depth 表示嵌套深度,好比結構體中嵌套的類型,其深度就大於 0。 func (p *pp) printValue(value reflect.Value, verb rune, depth int) { // 嵌套中的值,即便有自定義的格式化方法,也不會被 printArg 調用,在這裏進行調用 if depth > 0 && value.IsValid() && value.CanInterface() { p.arg = value.Interface() if p.handleMethods(verb) { return } } p.arg = nil // 傳入的是反射值,反射值不必定是經過 arg 獲取到的,清空防干擾 p.value = value // 記下 value 內容,方便其它地方使用 switch f := value; value.Kind() { case reflect.Invalid: if depth == 0 { // 非嵌套的無效值 p.buf.WriteString(invReflectString) // "<invalid reflect.Value>" } else { // 嵌套的無效值 switch verb { case 'v': p.buf.WriteString(nilAngleString) // "<nil>" default: p.badVerb(verb) } } // 不一樣類型的處理方法和 printArg 中相同。 case reflect.Bool: p.fmtBool(f.Bool(), verb) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p.fmtInteger(uint64(f.Int()), signed, verb) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p.fmtInteger(f.Uint(), unsigned, verb) case reflect.Float32: p.fmtFloat(f.Float(), 32, verb) case reflect.Float64: p.fmtFloat(f.Float(), 64, verb) case reflect.Complex64: p.fmtComplex(f.Complex(), 64, verb) case reflect.Complex128: p.fmtComplex(f.Complex(), 128, verb) case reflect.String: p.fmtString(f.String(), verb) // 嵌套類型 map,須要遞歸 case reflect.Map: if p.fmt.sharpV { // Go 語法格式 p.buf.WriteString(f.Type().String()) if f.IsNil() { p.buf.WriteString(nilParenString) // "(nil)" return } p.buf.WriteByte('{') } else { p.buf.WriteString(mapString) // "map[" } keys := f.MapKeys() for i, key := range keys { if i > 0 { // 第一個元素前面不添加分隔符 if p.fmt.sharpV { // Go 語法格式 p.buf.WriteString(commaSpaceString) // ", " } else { p.buf.WriteByte(' ') } } p.printValue(key, verb, depth+1) // 遞歸處理鍵 p.buf.WriteByte(':') p.printValue(f.MapIndex(key), verb, depth+1) // 遞歸處理值 } if p.fmt.sharpV { // Go 語法格式 p.buf.WriteByte('}') } else { p.buf.WriteByte(']') } // 嵌套類型 struct,須要遞歸 case reflect.Struct: if p.fmt.sharpV { // Go 語法格式 p.buf.WriteString(f.Type().String()) } p.buf.WriteByte('{') for i := 0; i < f.NumField(); i++ { if i > 0 { // 第一個元素前面不添加分隔符 if p.fmt.sharpV { // Go 語法格式 p.buf.WriteString(commaSpaceString) // ", " } else { p.buf.WriteByte(' ') } } // 結構體字段語法格式 || Go 語法格式(類型:值) if p.fmt.plusV || p.fmt.sharpV { if name := f.Type().Field(i).Name; name != "" { p.buf.WriteString(name) p.buf.WriteByte(':') } } // 遞歸處理成員 p.printValue(getField(f, i), verb, depth+1) } p.buf.WriteByte('}') // 接口(包含實現該接口的對象),須要遞歸 case reflect.Interface: value := f.Elem() // 獲取接口中的對象 if !value.IsValid() { if p.fmt.sharpV { // Go 語法格式 p.buf.WriteString(f.Type().String()) p.buf.WriteString(nilParenString) // "(nil)" } else { p.buf.WriteString(nilAngleString) // "<nil>" } } else { // 遞歸處理接口中的對象 p.printValue(value, verb, depth+1) } // 嵌套類型 map、slice,可能須要遞歸(若是元素不是字節的話) case reflect.Array, reflect.Slice: switch verb { case 's', 'q', 'x', 'X': // 處理字節類型的切片和數組,它們對於上面的動詞而言比較特殊,不用遞歸。 t := f.Type() if t.Elem().Kind() == reflect.Uint8 { // 判斷元素類型 var bytes []byte if f.Kind() == reflect.Slice { // 切片類型(直接獲取字節內容) bytes = f.Bytes() } else if f.CanAddr() { // 能夠轉換成切片 bytes = f.Slice(0, f.Len()).Bytes() } else { // 不能轉換爲切片 // 數組不能 Slice(),因此手動創建了一個切片,這是一種罕見的狀況。 bytes = make([]byte, f.Len()) for i := range bytes { bytes[i] = byte(f.Index(i).Uint()) } } // 寫入獲取到的字節內容 p.fmtBytes(bytes, verb, t.String()) return } } if p.fmt.sharpV { // Go 語法格式 p.buf.WriteString(f.Type().String()) if f.Kind() == reflect.Slice && f.IsNil() { p.buf.WriteString(nilParenString) // "(nil)" return } else { p.buf.WriteByte('{') for i := 0; i < f.Len(); i++ { if i > 0 { // 第一個元素前面不添加分隔符 p.buf.WriteString(commaSpaceString) // ", " } // 遞歸處理元素 p.printValue(f.Index(i), verb, depth+1) } p.buf.WriteByte('}') } } else { p.buf.WriteByte('[') for i := 0; i < f.Len(); i++ { if i > 0 { // 第一個元素前面不添加分隔符 p.buf.WriteByte(' ') } // 遞歸處理元素 p.printValue(f.Index(i), verb, depth+1) } p.buf.WriteByte(']') } // 指針(指向具體的對象),須要遞歸 case reflect.Ptr: // 只處理最外層的指針,不處理嵌套中的指針(避免循環) if depth == 0 && f.Pointer() != 0 { // 獲取指針指向的元素 switch a := f.Elem(); a.Kind() { // 數組、切片、結構、映射 case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map: p.buf.WriteByte('&') // 遞歸處理指針指向的元素 p.printValue(a, verb, depth+1) return } } // 嵌套指針交給下面的代碼處理 fallthrough // 通道、函數、不安全指針,只寫入指針地址。 case reflect.Chan, reflect.Func, reflect.UnsafePointer: p.fmtPointer(f, verb) default: p.unknownType(f) } } // 以後的代碼用於解析格式字符串中的「佔位符」 // 從指定 arg 中讀取整數值(用於提供給精度或寬度)。 // a:arg 列表。argNum:要獲取的 arg 下標 // num:獲取到的值。isInt:arg 是否爲整型。 // newArgNum:成功則返回 argNum+1,失敗則返回 argNum。 func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int) { newArgNum = argNum if argNum < len(a) { num, isInt = a[argNum].(int) // 一般這裏都會轉換成功,從而跳過下面一大堆代碼。 if !isInt { switch v := reflect.ValueOf(a[argNum]); v.Kind() { // 有符號整型 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: n := v.Int() // 根據平臺的不一樣,在 int 範圍內進行判斷 if int64(int(n)) == n { num = int(n) isInt = true } // 無符號整型 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: n := v.Uint() // 根據平臺的不一樣,在 int 範圍內進行判斷 if int64(n) >= 0 && uint64(int(n)) == n { num = int(n) isInt = true } default: // 默認值 num = 0, isInt = false. } } newArgNum = argNum + 1 // 範圍檢查 if tooLarge(num) { num = 0 isInt = false } } return } // 解析 arg 索引,將 [] 中的字符串解析成數值再減 1 後返回。 // format 必須以「[」開頭,「]」的位置則會經過查找肯定。 // 由於 arg 索引是從 1 開始的,而 arg 下標則是從 0 開始的,因此要減 1。 // index:解析結果。wid:索引字符串的總長度(包括中括號)。ok:是否成功。 // 若是 [] 爲空或沒有結束括號「]」,則解析失敗,index 無效,wid 返回 1。 // wid 返回 1 表示跳過無效的起始括號「[」,繼續處理以後的內容。 // 若是 [] 中的內容不全是數字,則解析失敗,index 無效,wid 正常返回。 func parseArgNumber(format string) (index int, wid int, ok bool) { // 長度至少 3 個字節: [n]。 if len(format) < 3 { return 0, 1, false } // 查找結束括號「]」 for i := 1; i < len(format); i++ { if format[i] == ']' { // 解析 [] 之間的內容。 // width:解析結果。ok:是否解析成功。newi:待處理下標 width, ok, newi := parsenum(format, 1, i) // 解析失敗,或者 n 中含有非法字符。 if !ok || newi != i { return 0, i + 1, false } // 解析成功 return width - 1, i + 1, true } } // 沒有結束括號「]」 return 0, 1, false } // 解析 format 中的一個 arg 索引,i 標識索引字符串的起始位置, // 解析過程是經過 parseArgNumber 完成的,這裏只進行一些檢查、控制等操做。 // argNum:要解析的 arg 下標。numArgs:arg 總個數。 // newArgNum:解析結果。newi:待處理下標。found:是否找到 [] 而且中間全是數字。 func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) { // format[i] 必須是「[」字符,不然按原值返回,不予處理 if len(format) <= i || format[i] != '[' { return argNum, i, false } // 標記使用了指定的 argNum,原 argNum 的順序被打亂。 p.reordered = true // 調用另外一個函數去處理 arg 索引 index, wid, ok := parseArgNumber(format[i:]) // 解析成功,而且解析出來的 arg 索引在合法範圍內。 if ok && 0 <= index && index < numArgs { return index, i + wid, true } // 解析失敗,或者解析出來的 arg 索引超出合法範圍。 // 標記當前 arg 索引無效 p.goodArgNum = false // ok 可能爲 true(好比結果超出範圍,但這符合 found 的要求) return argNum, i + wid, ok } // 無效arg索引 func (p *pp) badArgNum(verb rune) { p.buf.WriteString(percentBangString) // "%!" p.buf.WriteRune(verb) p.buf.WriteString(badIndexString) // "(BADINDEX)" } // 未找到相應arg func (p *pp) missingArg(verb rune) { p.buf.WriteString(percentBangString) // "%!" p.buf.WriteRune(verb) p.buf.WriteString(missingString) // "(MISSING)" } // 解析佔位符並格式化相應 arg,以替換佔位符。 func (p *pp) doPrintf(format string, a []interface{}) { end := len(format) // 用於範圍檢查 argNum := 0 // 正在處理的 arg 索引 afterIndex := false // 是否剛處理完佔位符中的 [n] p.reordered = false // 當前 argNum 是否由佔位符中 [n] 指定 formatLoop: // 每循環一次,處理一個佔位符。 for i := 0; i < end; { // 復位有效性,新的 argNum 是有效的 p.goodArgNum = true // 一、寫入 % 以前的內容 lasti := i for i < end && format[i] != '%' { i++ } if i > lasti { p.buf.WriteString(format[lasti:i]) } // 若是到達尾部,則全部佔位符都處理完畢,返回 if i >= end { break } // 跳過 % 字符,正式進入一個「佔位符」的處理流程 i++ // 復位旗標,準備從新登記 p.fmt.clearflags() simpleFormat: // 二、處理旗標 [#0+- ] for ; i < end; i++ { c := format[i] switch c { case '#': p.fmt.sharp = true case '0': // 0 只容許填充在左邊(不能與 "-" 共存) p.fmt.zero = !p.fmt.minus // "-" 優先於 "0" case '+': p.fmt.plus = true case '-': // 0 只容許填充在左邊(不能與 "-" 共存) p.fmt.minus = true p.fmt.zero = false // "-" 優先於 "0" case ' ': p.fmt.space = true default: // 沒有指定「精度、寬度、[n]」的簡單佔位符,能夠在這裏快速處理。 // 這是常見的佔位符,單獨處理能夠提升效率。 if 'a' <= c && c <= 'z' && argNum < len(a) { if c == 'v' { p.fmt.sharpV = p.fmt.sharp // Go 語法格式 #v p.fmt.sharp = false // # 再也不具備默認的含義 p.fmt.plusV = p.fmt.plus // 結構體字段語法格式 +v p.fmt.plus = false // + 再也不具備默認的含義 } p.printArg(a[argNum], rune(c)) // 分析完畢,處理當前 arg argNum++ // 準備處理下一個 arg i++ // 跳過剛處理的動詞 continue formatLoop // 繼續處理下一個佔位符 } // 更復雜的佔位符(交給後面的代碼繼續分析) break simpleFormat } } // 旗標已經處理過了,接下來處理佔位符中的 [n]、寬度、精度信息 // 處理 [n](這裏處理 [n] 是爲後面處理 [n]* 寬度信息作準備, // 由於要從相應的 arg 中讀取寬度值) argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) // argNum:獲取到的 [n] 中的值。i:待處理下標。 // afterIndex:是否剛處理完 [n] // 處理從 arg 中獲取的寬度信息 * // * 表示用 args[argNum] 的值做爲寬度值來格式化 args[argNum+1] if i < end && format[i] == '*' { i++ // 跳過 * 號 // wid:args[argNum] 的值。widPresent:wid 是否設置成功。 // argNum:argNum+1(不管 wid 是否設置成功,只要不超出 args 數量) p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum) // 寬度值設置失敗,處理無效寬度(僅顯示一個提示,不影響其它部分的處理) if !p.fmt.widPresent { p.buf.WriteString(badWidthString) // "%!(BADWIDTH)" } // 處理獲取到的負寬度值,將其轉換爲正數,並設置 "-" 旗標爲 true if p.fmt.wid < 0 { p.fmt.wid = -p.fmt.wid p.fmt.minus = true p.fmt.zero = false // 0 只容許填充在左邊(不能與 "-" 共存) } afterIndex = false // 剛處理的是寬度信息 *,再也不是 [n] } else { // 儘量解析遇到的數字 // wid:解析結果。widPresent:是否解析成功。i:待處理下標 p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) // [n] 必須在寬度信息以後(這就是 afterIndex 的做用) if afterIndex && p.fmt.widPresent { // 避免 "%[3]2d" p.goodArgNum = false } } // 處理精度信息 if i+1 < end && format[i] == '.' { i++ // 跳太小數點 // [n] 必須在精度信息以後 // 若是沒有設置寬度信息,可能會出現下面的錯誤寫法 if afterIndex { // 避免 "%[3].2d" p.goodArgNum = false } // 處理 [n](這裏處理 [n] 是爲後面處理 [n]* 精度值作準備, // 由於要從相應的 arg 中讀取精度值) argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) // 處理從 arg 中獲取的精度信息 * // * 表示用 args[argNum] 的值做爲精度值來格式化 args[argNum+1] if i < end && format[i] == '*' { i++ // 跳過 * 號 // prec:args[argNum] 的值。precPresent:prec 是否設置成功。 // argNum:argNum+1(不管 prec 是否設置成功,只要不超出 args 數量) p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum) // 負精度值沒有意義 if p.fmt.prec < 0 { p.fmt.prec = 0 p.fmt.precPresent = false } // 精度值設置失敗,處理無效精度(僅顯示一個提示,不影響其它部分的處理) if !p.fmt.precPresent { p.buf.WriteString(badPrecString) // "%!(BADPREC)" } afterIndex = false // 剛處理的是精度信息 *,再也不是 [n]。 } else { // 儘量解析遇到的數字 // prec:解析結果。precPresent:是否解析成功。i:待處理下標。 p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end) // 若是沒有指定精度值,則默認精度爲 0。 if !p.fmt.precPresent { p.fmt.prec = 0 // 這個好像不用設置,parsenum 失敗則 prec 確定爲 0。 p.fmt.precPresent = true // 針對 fmt.Printf("%#8.d", 0) 的狀況 // 這裏多是一個小疏忽,忽略了 "%.[3]2d" 這種錯誤的寫法。 // 下面一行代碼通過了修改,而且增長了 2 行代碼。 // [n] 必須在精度值以後。 } else if afterIndex && !p.fmt.precPresent { // 拒絕 "%.[3]2d",容許 "%.[2]f" p.goodArgNum = false } } } // 在寬度信息和精度信息以後能夠指定新的 [n],用以指示要格式化的 arg,因此 // 這裏須要再次獲取 [n]。若是以前沒有處理寬度和精度信息,那麼 [n] 在處理 // 寬度信息以前就已經獲取過了,這裏就不須要再次獲取。 if !afterIndex { argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) } // 若是 arg 索引以後沒有了內容,則說明缺乏必要的動詞 if i >= end { p.buf.WriteString(noVerbString) // "%!(NOVERB)" break } // 獲取最後的動詞 verb, w := utf8.DecodeRuneInString(format[i:]) i += w // 跳過最後的動詞 // 處理特殊動詞和錯誤信息 switch { case verb == '%': // %% 解析爲一個 % 寫入 p.buf.WriteByte('%') case !p.goodArgNum: // argNum 無效(因爲 [n] 指定錯誤,或者放錯了位置) p.badArgNum(verb) case argNum >= len(a): // argNum 超出範圍(通常由於 arg 數量不夠) p.missingArg(verb) case verb == 'v': // 特殊動詞 #v 和 +v // Go 語法格式 p.fmt.sharpV = p.fmt.sharp p.fmt.sharp = false // 結構體字段語法格式 p.fmt.plusV = p.fmt.plus p.fmt.plus = false fallthrough default: // 開始解析 arg p.printArg(a[argNum], verb) // 準備處理下一個 arg argNum++ } } // 全部佔位符都處理完畢 // 檢查多餘的 arg(提供的 arg 過多) // 若是在佔位符中使用了 [n],則跳過這裏的檢查 if !p.reordered && argNum < len(a) { p.fmt.clearflags() // 清空旗標,開始處理多餘的 arg p.buf.WriteString(extraString) // "%!(EXTRA " for i, arg := range a[argNum:] { if i > 0 { // 第一個 arg 以前不添加分隔符 p.buf.WriteString(commaSpaceString) // ", " } if arg == nil { p.buf.WriteString(nilAngleString) } else { p.buf.WriteString(reflect.TypeOf(arg).String()) p.buf.WriteByte('=') p.printArg(arg, 'v') // 使用 v 動詞處理全部 arg } } p.buf.WriteByte(')') } } // 格式化 a 中提供的 arg,在非字符串 arg 之間添加空格 func (p *pp) doPrint(a []interface{}) { prevString := false for argNum, arg := range a { isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String // 在全部非字符串 arg 之間添加空格 if argNum > 0 && !isString && !prevString { p.buf.WriteByte(' ') } p.printArg(arg, 'v') // 使用 v 動詞處理全部 arg prevString = isString } } // 格式化 a 中提供的 arg,在全部 arg 之間添加空格,並在最後添加換行符 func (p *pp) doPrintln(a []interface{}) { for argNum, arg := range a { // 在全部 arg 之間添加空格 if argNum > 0 { p.buf.WriteByte(' ') } p.printArg(arg, 'v') // 使用 v 動詞處理全部 arg } p.buf.WriteByte('\n') // 尾部添加換行符 }