Go - io 標準函式庫學習筆記
關於 Go 語言 io 標準函式庫的一些閱讀後的筆記
Reader 的定義
Reader 是一個包裝了基本 Read 方法的 interface。
Read 讀取
len(p)位元組到p,它會回傳位元組數n(0 <= n <= len(p))和任何遇到的錯誤。即使 Read 回傳的n小於len(p),它也會在調用過程中,使用 p 作為暫存空間。如果一些資料可以使用,但是不滿足len(p)位元組長度,一般 Read 會回傳可使用的資料,而不是繼續等待更多的資料。當 Read 在成功讀取
n > 0位元組遇到錯誤,或者是 EOF(end-of-file) 狀態時,它會回傳讀取到的位元組數。它會從相同的調用回傳非 nil 的錯誤或者是從一個隨後調用中回傳錯誤(n 同時為 0)。一般常見的情況是,一個 Reader 在 input stream 結束時,回傳一個非 0 的位元組長度,可能是err == EOF或是err == nil。下一個 Read 應該回傳0, EOF。Callers 總是要考慮到在錯誤 err 之前,應該處理回傳的
n > 0位元組長度。這樣做可以正確處理在讀取一些位元組後所發生的 I/O 錯誤,同時也允許 EOF 行為。Read 的實作不希望回傳一個
0位元組的長度和一個nil的 error,除非當len(p) == 0。Callers 應該對於一個回傳0和nil作為什麼事都沒發生,特別是它並不表示是 EOF。
Reader 的 interface 定義:
type Reader interface {Read(p []byte) (n int, err error)}
範例:
// Source: https://github.com/polaris1119/The-Golang-Standard-Library-by-Example/blob/master/chapter01/01.1.mdfunc ReadFrom(reader io.Reader, num int) ([]byte, error) {p := make([]byte, num)n, err := reader.Readerif n > 0 {retrun p[:n], nil}return p, err}func main() {data, err := ReadFrom(strings.NewReader("Write Something...", 20))if err != nil {log.Fatal(err)}fmt.Println(string(data))}
ReadFrom 函式接受 reader 和 num 參數,其中 reader 的型別是 io.Reader,所以只要有實作 io.Reader 這個型別都可以作為參數傳入,函式最後回傳了 ([]byte, error)。
在 ReadFrom 函式的第五行,我們可以取得實作 io.Reader 的 reader 所回傳的位元組長度以及錯誤;在第十四行,我們使用了 strings 標準函式庫的 NewReader 方法,根據文件說明 strings.NewReader 回傳一個新的 Reader,詳細請參考原始碼,所以這符合傳入 ReadFrom 的第一個參數所要求的型別。
Writer 的定義
Writer 是一個包裝基本 Write 方法的 interface。
Write 從 p 寫入
len(p)位元組長度到底層的 data stream。它從 p 回傳(0 <= n <= len(p))的位元組數以及任何遇到造成寫入提早停止的任何錯誤。如果回傳
n < len(p),Write 必須回傳一個非nil的錯誤。即使暫停,Write 也不能修改 slice 資料。
Writeer 的 interface 定義:
type Writer interface {Write(p []byte) (n int, err error)}
範例:
func main() {var b bytes.Bufferb.Write([]byte("Hello! "))fmt.Fprint(&b, "My name is P.J.")b.WriteTo(os.Stdout)}
這裡我們使用 bytes.Buffer 作為範例,因為 Buffer 實作了 io 的方法,在第三行我們對 Write 方法傳入了一個 slice,fmt.Fprint 第一個參數接收一個 io.Writer 型別的參數,它會將第二個參數的內容寫入到第一個參數的 slice 內,實際上 fmt.Fprint 的第二個參數是一個可變參數,也就是說我也可以傳入像是 fmt.Fprint(&b, 1, "Hi", 2, "Ho") 帶有多個參數,詳細可以參考 fmt.Fprint 的定義,最後我們使用了 b.WriteTo(os.Stdout) 輸出訊息到終端機。
os.Stdout 是來自 os 標準函式庫內的變數:
var (Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr"))
實際上,Stdin、Stdout、Stderr 是對應到 Linux 下的輸入、輸出,和錯誤描述,根據 NewFile 的定義,它們都回傳了 *File 這個 type,而 *File 也都實作了 io 的 Reader 和 Writer。
Go 的
io標準函式庫提供了最基本的 interface,它主要的工作是封裝一些現有原始(primitive)的實作,把抽象的功能轉換爲公開的 interface,所以我們並不需要關心具體的邏輯。
心得
我們在撰寫程式碼時,需要注意方法所接收的參數型別,例如某個函式的定義是 func someFn(r io.Reader),那麼我們傳入的 r 就必須一定就必須一定要實作 io.Reader,我們不需要關心實際的實作細節,例如在 Reader 的範例;如果同時實作了 io.Reader 和 io.Writer,就可以進行資料的讀取和寫入囉。