IO 操作的基本分類
在計算機中,處理文件和網絡通訊等,都需要進行 IO 操作,IO 即是 input/ouput,計算機的輸入輸出操作。
Go語言中的 IO 操作封裝在如下幾個包中:
- io 為 IO 原語提供基本的接口
- io/ioutil 封裝一些實用的 I/O 函數
- fmt 實現了 I/O 的格式化
- bufio 實現了帶緩沖的 I/O
- net.Conn 網絡的讀寫
- os.Stdin, Stdout 系統標准輸入輸出
- os.File 系統文件操作
當然除了上面這些,還有一些比如 json,xml,tar 等也屬於文件操作。
其中 io 包中的 io.Reader 和 io.Writer 比較關鍵。io.Reader
接口實現了對文件、套接字等輸入設備的抽象;io.Writer
接口則實現了對輸出設備的抽象。
io 包提供了很多功能,這個包可以以流式的方式高效處理數據,而不用考慮數據是什么,數據來自哪里,以及數據要發送到哪里去。只要你實現了這2個接口。這就是抽象的能力。
IO包2個重要接口 Reader 和 Writer
在 io 包中有 2 個重要的接口:io.Reader
和 io.Writer
。
實現了這2個接口,就可以使用 io
包的功能。
Reader 接口
type Reader interface {
Read(p []byte) (n int, err error)
}
Read() 方法將 len(p) 個字節讀取到 p 中。它返回讀取的字節數 n,以及發生錯誤時的錯誤信息。
- 如果讀到了數據(n > 0),則 err 應該返回 nil。
- 如果數據被讀空,沒有數據可讀(n == 0),則 err 應該返回 EOF。
- 如果遇到讀取錯誤,則 err 應該返回相應的錯誤信息。
- n 可能小於 len(p),也就是說在 Go 讀取 IO 時,是不會保證一次讀取預期的所有數據的。
- 如果要保證讀取所需的所有數據,就需要在一個循環里調用 Read,累加每次返回的數據。
只要某個實例實現了接口 io.Reader 里的方法 Read() ,就滿足了接口 io.Reader ,可以當做參數傳入進來。
io.EOF 變量的定義:var EOF = errors.New("EOF"),是 error 類型。根據 reader 接口的說明,在 n > 0 且數據被讀完了的情況下,當次返回的 error 有可能是 EOF 也有可能是 nil。
例子1: demo_reader.go 從標准輸入中讀取數據
package main
import (
"fmt"
"io"
"os"
)
func ReadFrom(reader io.Reader, num int) ([]byte, error) {
p := make([]byte, num)
n, err := reader.Read(p)
fmt.Println("n: ", n)
if n > 0 {
return p[:n], nil
}
return p, err
}
func main() {
for {
data, err := ReadFrom(os.Stdin, 4)
if err != nil {
if err == io.EOF {
break
}
} else {
fmt.Printf("receive: %X, %s\n", data, string(data))
}
}
}
Writer 接口
type Writer interface {
Write(p []byte) (n int, err error)
}
Write 方法將 len(p) 個字節從 p 中寫入到對象數據流中。它返回從 p 中被寫入的字節數 n,以及發生錯誤時返回的錯誤信息。
- 如果 p 中的數據全部被寫入,則 err 應該返回 nil。
- 如果 p 中的數據無法被全部寫入,則 err 應該返回相應的錯誤信息。
例子1 :demo_writer.go
package main
import (
"bytes"
"fmt"
"os"
)
func main() {
// 創建 Buffer 暫存空間,並將一個字符串寫入 Buffer
// 使用 io.Writer 的 Write 方法寫入
var buf bytes.Buffer
buf.Write([]byte("hello world , "))
// 用 Fprintf 將一個字符串拼接到 Buffer 里
fmt.Fprintf(&buf, " welcome to golang !")
// 將 Buffer 的內容輸出到標准輸出設備
buf.WriteTo(os.Stdout)
}
bytes.Buffer 結構體:
bytes.Buffer 是一個結構體類型,用來暫存寫入的數據,這個結構體實現了 io.Writer 接口的 Write 方法。
Fprintf 方法定義:
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
第一個參數是 io.Writer 接口類型,bytes.Buffer 結構體實現了 io.Writer 接口里的 Write 方法,實現了 Write 方法的類型都可以作為參數(這里是 buf)傳入。
WriteTo 方法定義:
func (b *Buffer) WriteTo(w io.Writer) (n int64, err error)
WriteTo 方法第一個參數是 io.Writer 接口類型。
例子1:file_write.go
package main
import (
"fmt"
"os"
)
func main() {
strings := []string{
"hello, golang! \n",
"welcome to golang! \n",
"Go is a good lang. ",
}
file, err := os.Create("./writefile.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close()
for _, p := range strings {
// file 類型實現了 io.Writer
n, err := file.Write([]byte(p))
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if n != len(p) {
fmt.Println("failed to write data")
os.Exit(1)
}
}
fmt.Println("file wirte done")
}
把 strings 這個 slice 結構中的字符串寫入到名為 writefile.txt 的文件中。