------------------------------------------------------------ 【格式化輸出】 // 格式化輸出:將 arg 列表中的 arg 轉換爲字符串輸出 // 使用動詞 v 格式化 arg 列表,非字符串元素之間添加空格 Print(arg列表) // 使用動詞 v 格式化 arg 列表,全部元素之間添加空格,結尾添加換行符 Println(arg列表) // 使用格式字符串格式化 arg 列表 Printf(格式字符串, arg列表) // Print 類函數會返回已處理的 arg 數量和遇到的錯誤信息。 ------------------------------------------------------------ 【格式字符串】 格式字符串由普通字符和佔位符組成,例如: "abc%+ #8.3[3]vdef" 其中 abc 和 def 是普通字符,其它部分是佔位符,佔位符以 % 開頭(注:%% 將被轉義爲一個普通的 % 符號,這個不算開頭),以動詞結尾,格式以下: %[旗標][寬度][.精度][arg索引]動詞 方括號中的內容能夠省略。 ------------------------------------------------------------ 【旗標】 旗標有如下幾種: + :對於數值類型老是輸出正負號(其它用法在動詞部分說明)。 - :在右邊進行寬度填充,而不是默認的左邊。 空格:對於數值類型的正數,保留一個空白的符號位(其它用法在動詞部分說明)。 0 :用 0 進行寬度填充而不用空格,對於數值類型,符號將被移到全部 0 的前面。 # :相關用法在動詞部分說明。 其中 "0" 和 "-" 不能同時使用,優先使用 "-" 而忽略 "0"。 ------------------------------------------------------------ 【寬度和精度】 「寬度」和「精度」均可以寫成如下三種形式: 數值 | * | arg索引* 其中「數值」表示使用指定的數值做爲寬度值或精度值,「*」表示使用當前正在處理的 arg 的值做爲寬度值或精度值,若是這樣的話,要格式化的 arg 將自動跳轉到下一個。「arg索引*」表示使用指定 arg 的值做爲寬度值或精度值,若是這樣的話,要格式化的 arg 將自動跳轉到指定 arg 的下一個。 寬度值:用於設置最小寬度。 精度值:對於浮點型,用於控制小數位數,對於字符串或字節數組,用於控制字符數量(不是字節數量)。 對於浮點型而言,動詞 g/G 的精度值比較特殊,在適當的狀況下,g/G 會設置總有效數字,而不是小數位數。 ------------------------------------------------------------ 【arg 索引】 「arg索引」由中括號和 arg 序號組成(就像上面示例中的 [3]),用於指定當前要處理的 arg 的序號,序號從 1 開始: '[' + arg序號 + ']' ------------------------------------------------------------ 【動詞】 「動詞」不能省略,不一樣的數據類型支持的動詞不同。 ------------------------------ [通用動詞] v:默認格式,不一樣類型的默認格式以下: 布爾型:t 整 型:d 浮點型:g 複數型:g 字符串:s 通 道:p 指 針:p #v:默認格式,以符合 Go 語法的方式輸出。特殊類型的 Go 語法格式以下: 無符號整型:x T:輸出 arg 的類型而不是值(使用 Go 語法格式)。 ------------------------------ [布爾型] t:輸出 true 或 false 字符串。 ------------------------------ [整型] b/o/d:輸出 2/8/10 進制格式 x/X :輸出 16 進制格式(小寫/大寫) c :輸出數值所表示的 Unicode 字符 q :輸出數值所表示的 Unicode 字符(帶單引號)。對於沒法顯示的字符,將輸出其轉義字符。 U :輸出 Unicode 碼點(例如 U+1234,等同於字符串 "U+%04X" 的顯示結果) 對於 o/x/X: 若是使用 "#" 旗標,則會添加前導 0 或 0x。 對於 U: 若是使用 "#" 旗標,則會在 Unicode 碼點後面添加相應的 '字符'(前提是該字符必須可顯示) ------------------------------ [浮點型和複數型] b :科學計數法(以 2 爲底) e/E:科學計數法(以 10 爲底,小寫 e/大寫 E) f/F:普通小數格式(二者無區別) g/G:大指數(指數 >= 6)使用 %e/%E,其它狀況使用 %f/%F ------------------------------ [字符串或字節切片] s :普通字符串 q :雙引號引發來的 Go 語法字符串 x/X:十六進制編碼(小寫/大寫,以字節爲元素進行編碼,而不是字符) 對於 q: 若是使用了 "+" 旗標,則將全部非 ASCII 字符都進行轉義處理。 若是使用了 "#" 旗標,則輸出反引號引發來的字符串(前提是 字符串中不包含任何製表符之外的控制字符,不然忽略 # 旗標) 對於 x/X: 若是使用了 " " 旗標,則在每一個元素之間添加空格。 若是使用了 "#" 旗標,則在十六進制格式以前添加 0x 前綴。 ------------------------------ [指針類型] p :帶 0x 前綴的十六進制地址值。 #p:不帶 0x 前綴的十六進制地址值。 ------------------------------ [符合類型] 複合類型將使用不一樣的格式輸出,格式以下: 結 構 體:{字段1 字段2 ...} 數組或切片:[元素0 元素1 ...] 映 射:map[鍵1:值1 鍵2:值2 ...] 指向符合元素的指針:&{}, &[], &map[] 複合類型自己沒有動詞,動詞將應用到複合類型的元素上。 結構體可使用 "+v" 同時輸出字段名。 ------------------------------------------------------------ 【完整示例】 package main import "fmt" func main() { // 旗標、寬度、精度、索引 fmt.Printf("|%0+- #[1]*.[2]*[3]d|%0+- #[1]*.[2]*[4]d|\n", 8, 4, 32, 64) // 浮點型精度 fmt.Printf("|%f|%8.4f|%8.f|%.4f|%.f|\n", 3.2, 3.2, 3.2, 3.2, 3.2) fmt.Printf("|%.3f|%.3g|\n", 12.345678, 12.345678) fmt.Printf("|%.2f|\n", 12.345678+12.345678i) // 字符串精度 s := "你好世界!" fmt.Printf("|%s|%8.2s|%8.s|%.2s|%.s|\n", s, s, s, s, s) fmt.Printf("|%x|%8.2x|%8.x|%.2x|%.x|\n", s, s, s, s, s) // 帶引號字符串 s1 := "Hello 世界!" // CanBackquote s2 := "Hello\n世界!" // !CanBackquote fmt.Printf("%q\n", s1) // 雙引號 fmt.Printf("%#q\n", s1) // 反引號成功 fmt.Printf("%#q\n", s2) // 反引號失敗 fmt.Printf("%+q\n", s2) // 僅包含 ASCII 字符 // Unicode 碼點 fmt.Printf("%U, %#U\n", '好', '好') fmt.Printf("%U, %#U\n", '\n', '\n') // 接口類型將輸出其內部包含的值 var i interface{} = struct { name string age int }{"AAA", 20} fmt.Printf("%v\n", i) // 只輸出字段值 fmt.Printf("%+v\n", i) // 同時輸出字段名 fmt.Printf("%#v\n", i) // Go 語法格式 // 輸出類型 fmt.Printf("%T\n", i) } ------------------------------------------------------------ 【注意】 一、若是 arg 是一個反射值,則該 arg 將被它所持有的具體值所取代。 二、若是 arg 實現了 Formatter 接口,將調用它的 Format 方法完成格式化。 三、若是 v 動詞使用了 # 旗標(%#v),而且 arg 實現了 GoStringer 接口,將調用它的 GoString 方法完成格式化。 若是格式化操做指定了字符串相關的動詞(好比 %s、%q、%v、%x、%X),接下來的兩條規則將適用: 4。若是 arg 實現了 error 接口,將調用它的 Error 方法完成格式化。 5。若是 arg 實現了 string 接口,將調用它的 String 方法完成格式化。 在實現格式化相關接口的時候,要避免無限遞歸的狀況,好比: type X string func (x X) String() string { return Sprintf("<%s>", x) } 在格式化以前,要先轉換數據類型,這樣就能夠避免無限遞歸: func (x X) String() string { return Sprintf("<%s>", string(x)) } 無限遞歸也可能發生在自引用數據類型上面,好比一個切片的元素引用了切片自身。這種狀況比較罕見,好比: a := make([]interface{}, 1) a[0] = a fmt.Println(a) ------------------------------------------------------------ 【格式化輸入】 // 格式化輸入:從輸入端讀取字符串(以空白分隔的值的序列), // 並解析爲具體的值存入相應的 arg 中,arg 必須是變量地址。 // 字符串中的連續空白視爲單個空白,換行符根據不一樣狀況處理。 // \r\n 被當作 \n 處理。 // 以動詞 v 解析字符串,換行視爲空白 Scan(arg列表) // 以動詞 v 解析字符串,換行結束解析 Scanln(arg列表) // 根據格式字符串中指定的格式解析字符串 // 格式字符串中的換行符必須和輸入端的換行符相匹配。 Scanf(格式字符串, arg列表) // Scan 類函數會返回已處理的 arg 數量和遇到的錯誤信息。 ------------------------------------------------------------ 【格式字符串】 格式字符串相似於 Printf 中的格式字符串,但下面的動詞和旗標例外: p :無效 T :無效 e/E/f/F/g/G:功能相同,都是掃描浮點數或複數 s/v :對字符串而言,掃描一個被空白分隔的子串 #/+ :無效 對於整型 arg 而言,v 動詞能夠掃描帶有前導 0 或 0x 的八進制或十六進制數值。 寬度被用來指定最大掃描寬度(不會跨越空格),精度不被支持。 若是 arg 實現了 Scanner 接口,將調用它的 Scan 方法掃描相應數據。只有基礎類型和實現了 Scanner 接口的類型可使用 Scan 類方法進行掃描。 ------------------------------------------------------------ 【完整示例】 // 對於 Scan 而言,回車視爲空白 func main() { a, b, c := "", 0, false fmt.Scan(&a, &b, &c) fmt.Println(a, b, c) // 在終端執行後,輸入 abc 1 回車 t 回車 // 結果 abc 1 true } // 整型變量也能夠解析八進制和十六進制 func main() { a, b, c := "", 0, false fmt.Scanln(&a, &b, &c) fmt.Println(a, b, c) // 在終端執行後,輸入 def 0x20 T 回車 // 結果 def 32 true } // 格式字符串能夠指定寬度 func main() { a, b, c := "", 0, false fmt.Scanf("%4s%d%t", &a, &b, &c) fmt.Println(a, b, c) // 在終端執行後,輸入 1234567true 回車 // 結果 1234 567 true } // 指定寬度不能跨越空白 func main() { a, b, c := "", 0, false fmt.Scanf("%4s%d%t", &a, &b, &c) fmt.Println(a, b, c) // 在終端執行後,輸入 12 34567True 回車 // 結果 12 34567 true } // %c 老是匹配下一個字符,包括空格 func main() { a, b, c := "", 0, 0 fmt.Scanf("%s%c%d", &a, &b, &c) fmt.Println(a, b, c) // 在終端執行後,輸入 abc 1 回車 // 結果 abc 32 1 } ------------------------------------------------------------ 【注意】 連續調用 FScan 可能會丟失數據,由於 FScan 中使用了 UnreadRune 對讀取的數據進行撤銷,而參數 io.Reader 只有 Read 方法,不支持撤銷。好比: package main import ( "fmt" "io" "strings" ) // 實現 Reader 接口的字符串類型 type stringReader string func (r *stringReader) Read(b []byte) (n int, err error) { n = copy(b, *r) *r = (*r)[n:] if n == 0 { err = io.EOF } return } func main() { // 下面的操做將丟失字符 a s := stringReader("123abcd") a, b := 0, "" fmt.Fscan(&s, &a) fmt.Fscan(&s, &b) fmt.Println(a, b) // 123 bcd // 若是使用 strings.NewReader 則不會丟失,由於它實現了 RuneScanner 接口 r := strings.NewReader("123abcd") c, d := 0, "" fmt.Fscan(r, &c) fmt.Fscan(r, &d) fmt.Println(c, d) // 123 abcd } ------------------------------------------------------------