以前的一篇有關終端讀寫的內容比較詳細:
https://blog.51cto.com/steed/2315597服務器
這篇,先補充了兩個從終端逐行掃描並處理的示例。而後再補充了有關兩個寫入操做的內容。ide
關於bufio包,使用它能夠簡便和高效地處理輸入和輸出。其中一個最有用的特性是稱爲掃描器(Scanner)的類型,它能夠讀取輸入,以行或者單詞爲單位斷開,這是處理以行爲單位輸入內容的最簡單方式。
先聲明一個 bufio.Scanner 類型的變量:函數
input := bufio.NewScanner(os.Stdin)
掃描器從程序的標準輸入進行讀取。每一次調用 inout.Scan() 讀取下一行,而且將結尾的換行符去掉;經過調用 input.Text() 來獲取讀到的內容。Scan 方法在讀到新行的時候返回true,在沒有更多內容的時候返回flase。
下面的例子,從標準輸入獲取到內容後,轉成全大寫再打印出來。Windows下使用 Ctrl+Z 後回車能夠退出:工具
package main import ( "bufio" "fmt" "os" "strings" ) func main() { input := bufio.NewScanner(os.Stdin) for input.Scan() { fmt.Println(strings.ToUpper(input.Text())) } }
上面的示例是每從標準輸入獲取到一行的內容,就進行處理。還有一種作法是,從標準輸入獲取所有的內容後(Ctrl+Z 後回車表示輸入完成),最後再一次所有輸出。下面的示例是從標準輸入獲取到所有內容,而後所有輸出,每一行的內容之間插入一個空格。最後也會有一個空格,輸出時把最後一個空格截斷:性能
package main import ( "bufio" "bytes" "fmt" "os" ) func main() { input := bufio.NewScanner(os.Stdin) buff := bytes.NewBuffer(nil) for input.Scan() { buff.Write(input.Bytes()) // 忽略錯誤 buff.WriteByte(' ') } fmt.Printf("%q\n", buff.String()[:buff.Len()-1]) // 截掉最後一個空格 }
使用io.Copy函數讀取響應的內容,好比直接複製內容到標準輸出,這樣就不須要把數據流裝到緩衝區:code
n, err := io.Copy(os.Stdout, resp.Body)
還能夠經過寫入 ioutil.Discard 輸出流進行丟棄,這樣作應該是爲了要有一個讀取的過程:blog
n, err := io.Copy(ioutil.Discard, resp.Body)
這裏要講的是經過接口類型斷言來查詢特性,下面是一個標準庫中使用的示例。而且,這個是向 io.Writer 寫入字符串的推薦方法。
下面定義一個方法,往 io.Writer 接口接入字符串信息:接口
func writeMsg(w io.Writer, msg string) error { if _, err := w.Write([]byte(msg)); err != nil { return err } return nil }
由於 Write 方法須要一個字節切片([]byte),而須要寫入的是一個字符串,因此要作類型轉換。這種轉換須要進行內存分配和內存複製,但複製後內存又會被立刻拋棄。這裏就會有性能問題,這個內存分配會致使性能降低,須要避開這個內存分配。
在不少包中,實現了 io.Writer 的重要類,都會提供一個對應的高效寫入字符串的 WriteString 方法,好比:內存
因爲 Writer 接口並不包括 WriteString 方法,不能直接調用。這裏能夠先定義一個新的接口,這個接口只包含 WriteString 方法,而後使用類型斷言來判斷 w 的動態類型是否知足這個新接口:字符串
// 將s寫入w,若是w有WriteString方法,就直接調用該方法 func writeString(w io.Writer, s string) (n int, err error) { type stringWriter interface { WriteString(string) (n int, err error) } if sw, ok := w.(stringWriter); ok { return sw.WriteString(s) // 避免了內存複製 } return w.Write([]byte(s)) // 分配了臨時內存 } func writeMsg(w io.Writer, msg string) error { if _, err := writeString(w, msg); err != nil { return err } return nil }
因爲上面的操做太常見了,io 包已經提供了一個函數 io.WriteString 能夠直接使用。上面的代碼能夠簡化爲以下的方式:
func writeMsg2(w io.Writer, msg string) error { if _, err := io.WriteString(w, msg); err != nil { return err } return nil }
這裏本質上仍是同樣的,接口的定義、類型檢查都封裝到了io包的內部,而且內部實現的邏輯和上面是同樣的。
以前想要向某個具體的類型寫入字符串的時候,會查看該類型是否有 WriteString 方法。可是若是要操做的類型是 io.Writer 接口,雖然實際背後的動態類型仍是那個類型,可是就沒法調用 WriteString 方法了。這時能夠直接使用 io 包提供的工具函數完成一樣的操做。
fmt.Fprint 系列的函數,接收的第一個參數是 io.Writer,也能夠進行寫入操做。並且 fmt 包提供的函數,接收空接口,能夠是任何的類型,自帶格式化成字符串的功能,不少時候更方便。 在向 os.Stdout 打印輸出錯誤,以及 Web 服務器向客戶端發送數據的時候使用的比較多,不過一樣的也是能夠向任何的 io.Writer 寫入內容的,好比寫入到文件。