基礎知識 - Golang 中的格式化輸入輸出


------------------------------------------------------------ 【格式化輸出】 // 格式化輸出:將 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) } ------------------------------------------------------------ 【注意】 1、如果 arg 是一個反射值,則該 arg 將被它所持有的具體值所取代。 2、如果 arg 實現了 Formatter 接口,將調用它的 Format 方法完成格式化。 3、如果 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 } ------------------------------------------------------------


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM