## 4.12\. 打印輸出
前面例子中涉及到的打印都比較簡單。在這一節中,我們將要討論Go語言格式化輸出的功能。
我們已經用過"fmt"包中的"Printf"和"Fprintf"等輸出函數。"fmt"包中的"Printf"函數的 完整說明如下:
```
Printf(format string, v ...) (n int, errno os.Error)
```
其中"..."表示數目可變參數,和C語言中"stdarg.h"中的宏類似。不過Go中,可變參數是通道 一個空接口("interface {}")和反射(reflection)庫實現的。反射特性可以幫助"Printf" 函數很好的獲取參數的詳細特征。
在C語言中,printf函數的要格式化的參數類型必須和格式化字符串中的標志一致。不過在Go語言中, 這些細節都被簡化了。我們不再需要"%llud"之類的標志,只用"%d"表示要輸出一個整數。至于對應 參數的實際類型,"Printf"可以通過反射獲取。例如:
```
10 var u64 uint64 = 1<<64-1
11 fmt.Printf("%d %d\n", u64, int64(u64))
```
輸出
```
18446744073709551615 -1
```
最簡單的方法是用"%v"標志,它可以以適當的格式輸出任意的類型(包括數組和結構)。下面的程序,
```
14 type T struct {
15 a int
16 b string
17 }
18 t := T{77, "Sunset Strip"}
19 a := []int{1, 2, 3, 4}
20 fmt.Printf("%v %v %v\n", u64, t, a)
```
將輸出:
```
18446744073709551615 {77 Sunset Strip} [1 2 3 4]
```
如果是使用"Print"或"Println"函數的話,甚至不需要格式化字符串。這些函數會針對數據類型 自動作轉換。"Print"函數默認將每個參數以"%v"格式輸出,"Println"函數則是在"Print"函數 的輸出基礎上增加一個換行。一下兩種輸出方式和前面的輸出結果是一致的。
```
21 fmt.Print(u64, " ", t, " ", a, "\n")
22 fmt.Println(u64, t, a)
```
如果要用"Printf"或"Print"函數輸出似有的結構類型,之需要為該結構實現一個"String()"方法, 返回相應的字符串就可以了。打印函數會先檢測該類型是否實現了"String()"方法,如果實現了則以 該方法返回字符串作為輸出。下面是一個簡單的例子。
```
09 type testType struct {
10 a int
11 b string
12 }
14 func (t *testType) String() string {
15 return fmt.Sprint(t.a) + " " + t.b
16 }
18 func main() {
19 t := &testType{77, "Sunset Strip"}
20 fmt.Println(t)
21 }
```
因為 *testType 類型有 String() 方法,因此格式化函數用它作為輸出結果:
```
77 Sunset Strip
```
前面的例子中,"String()"方法用到了"Sprint"(從字面意思可以猜測函數將返回一個字符串) 作為格式化的基礎函數。在Go中,我們可以遞歸使用"fmt"庫中的函數來為格式化服務。
"Printf"函數的另一種輸出是"%T"格式,它輸出的內容更加詳細,可以作為調試信息用。
自己實現一個功能完備,可以輸出各種格式和精度的函數是可能的。不過這不是該教程的重點,大家 可以把它當作一個課后練習。
讀者可能有疑問,"Printf"函數是如何知道變量是否有"String()"函數實現的。實際上,我們 需要先將變量轉換為Stringer接口類型,如果轉換成功則表示有"String()"方法。下面是一個 演示的例子:
```
type Stringer interface {
String() string
}
s, ok := v.(Stringer); // Test whether v implements "String()"
if ok {
result = s.String()
} else {
result = defaultOutput(v)
}
```
這里用到了類型斷言("v.(Stringer)"),用來判斷變量"v"是否可以滿足"Stringer"接口。 如果滿足,"s"將對應轉換后的Stringer接口類型并且"ok"被設置為"true"。然后我們通過"s", 以Stringer接口的方式調用String()函數。如果不滿足該接口特征,"ok"將被設置為false。
"Stringer"接口的命名通常是在接口方法的名字后面加e?r后綴,這里是"String+er"。
Go中的打印函數,除了"Printf"和"Sprintf"等之外,還有一個"Fprintf"函數。不過"Fprintf"函數和 的第一個參數并不是一個文件,而是一個在"io"庫中定義的接口類型:
```
type Writer interface {
Write(p []byte) (n int, err os.Error);
}
```
這里的接口也是采用類似的命名習慣,類型的接口還有"io.Reader"和"io.ReadWriter?"等。 在調用"Fprintf"函數時,可以用實現了"Write"方法的任意類型變量作為參數,例如文件、網絡、管道等等。
- 1. 關于本文
- 2. Go語言簡介
- 3. 安裝go環境
- 3.1. 簡介
- 3.2. 安裝C語言工具
- 3.3. 安裝Mercurial
- 3.4. 獲取代碼
- 3.5. 安裝Go
- 3.6. 編寫程序
- 3.7. 進一步學習
- 3.8. 更新go到新版本
- 3.9. 社區資源
- 3.10. 環境變量
- 4. Go語言入門
- 4.1. 簡介
- 4.2. Hello,世界
- 4.3. 分號(Semicolons)
- 4.4. 編譯
- 4.5. Echo
- 4.6. 類型簡介
- 4.7. 申請內存
- 4.8. 常量
- 4.9. I/O包
- 4.10. Rotting cats
- 4.11. Sorting
- 4.12. 打印輸出
- 4.13. 生成素數
- 4.14. Multiplexing
- 5. Effective Go
- 5.1. 簡介
- 5.2. 格式化
- 5.3. 注釋
- 5.4. 命名
- 5.5. 分號
- 5.6. 控制流
- 5.7. 函數
- 5.8. 數據
- 5.9. 初始化
- 5.10. 方法
- 5.11. 接口和其他類型
- 5.12. 內置
- 5.13. 并發
- 5.14. 錯誤處理
- 5.15. Web服務器
- 6. 如何編寫Go程序
- 6.1. 簡介
- 6.2. 社區資源
- 6.3. 新建一個包
- 6.4. 測試
- 6.5. 一個帶測試的演示包
- 7. Codelab: 編寫Web程序
- 7.1. 簡介
- 7.2. 開始
- 7.3. 數據結構
- 7.4. 使用http包
- 7.5. 基于http提供wiki頁面
- 7.6. 編輯頁面
- 7.7. template包
- 7.8. 處理不存在的頁面
- 7.9. 儲存頁面
- 7.10. 錯誤處理
- 7.11. 模板緩存
- 7.12. 驗證
- 7.13. 函數文本和閉包
- 7.14. 試試!
- 7.15. 其他任務
- 8. 針對C++程序員指南
- 8.1. 概念差異
- 8.2. 語法
- 8.3. 常量
- 8.4. Slices(切片)
- 8.5. 構造值對象
- 8.6. Interfaces(接口)
- 8.7. Goroutines
- 8.8. Channels(管道)
- 9. 內存模型
- 9.1. 簡介
- 9.2. Happens Before
- 9.3. 同步(Synchronization)
- 9.4. 錯誤的同步方式
- 10. 附錄
- 10.1. 命令行工具
- 10.2. 視頻和講座
- 10.3. Release History
- 10.4. Go Roadmap
- 10.5. 相關資源