------------------------------------------------------------ 先說一下接口,Go 語言中的接口很簡單,在 Go 語言的 io 包中有這樣一個函數: func ReadFull(r Reader, buf []byte) (n int, err error) 這個函數可以把對象 r 中的數據讀出來,然后存入一個緩沖區 buf 中,以便其它代碼可以處理 buf 中的數據。 這里有個問題,ReadFull 函數究竟可以讀取哪些對象的數據?可以讀文件中的數據嗎?可以讀網絡中的數據嗎?可以讀數據庫中的數據嗎?可以讀磁盤中的扇區嗎?可以讀內存中的數據嗎? 答案是 ReadFull 可以讀取任何對象的數據,但是有個前提,就是這個對象必須符合 Reader 的標准。 Reader 的標准是什么呢?下面是 Reader 的定義: type Reader interface { Read(p []byte) (n int, err error) } 從上面的定義可以看出,Reader 的標准很簡單,只要某個對象實現了 Read 方法,這個對象就符合了 Reader 的標准,就可以被 ReadFull 讀取。 太簡單了,只需要實現 Read 方法,不需要做其它任何事情。下面我們就來定義一個自己的類型,然后實現 Read 方法: ------------------------------ // 定義一個 Ustr 類型 type Ustr struct { s string // 數據流 i int // 讀寫位置 } // 根據字符串創建 Ustr 對象 func NewUstr(s string) *Ustr { return &Ustr{s, 0} } // 獲取未讀取部分的數據長度 func (s *Ustr) Len() int { return len(s.s) - s.i } // 實現 Ustr 類型的 Read 方法 func (s *Ustr) Read(p []byte) (n int, err error) { for ; s.i < len(s.s) && n < len(p); s.i++ { c := s.s[s.i] // 將小寫字母轉換為大寫字母,然后寫入 p 中 if 'a' <= c && c <= 'z' { p[n] = c + 'A' - 'a' } else { p[n] = c } n++ } // 根據讀取的字節數設置返回值 if n == 0 { return n, io.EOF } return n, nil } ------------------------------ 接下來,我們就可以用 ReadFull 方法讀取 Ustr 對象的數據了: ------------------------------ func main() { s := NewUstr("Hello World!") // 創建 Ustr 對象 s buf := make([]byte, s.Len()) // 創建緩沖區 buf n, err := io.ReadFull(s, buf) // 將 s 中的數據讀取到 buf 中 fmt.Printf("%s\n", buf) // HELLO WORLD! fmt.Println(n, err) // 12 <nil> } ------------------------------ 我們很快就實現了 Reader 的要求,這個 Reader 就是一個接口,接口就是一個標准,一個要求,一個規定,這個規定就是“要實現接口中的方法”。只要某個對象符合 Reader 接口的要求,那么這個對象就可以當作 Reader 接口來使用,就可以傳遞給 ReadFull 方法。 所以,只要文件對象實現了 Read 方法,那么 ReadFull 就可以讀取文件中的數據,只要網絡對象實現了 Read 方法,ReadFull 就可以讀取網絡中的數據,只要數據庫實現了 Read 方法,ReadFull 就可以讀取數據庫中的數據,只要磁盤對象實現了 Read 方法,ReadFull 就可以讀磁盤中的數據,只要內存對象實現了 Read 方法,ReadFull 就可以讀取內存中的數據,只要任何一個對象實現了 Read 方法,ReadFull 就可以讀取該對象的數據。 在 io 包中,定義了許多基本的接口類型,Go 語言的標准庫中大量使用了這些接口(就像 ReadFull 一樣使用它們),下面我們就來看一看都有哪些接口: ------------------------------------------------------------ // Reader 接口包裝了基本的 Read 方法,用於輸出自身的數據。 // Read 方法用於將對象的數據流讀入到 p 中,返回讀取的字節數和遇到的錯誤。 // 在沒有遇到讀取錯誤的情況下: // 1、如果讀到了數據(n > 0),則 err 應該返回 nil。 // 2、如果數據被讀空,沒有數據可讀(n == 0),則 err 應該返回 EOF。 // 如果遇到讀取錯誤,則 err 應該返回相應的錯誤信息。 type Reader interface { Read(p []byte) (n int, err error) } ------------------------------ // Writer 接口包裝了基本的 Write 方法,用於將數據存入自身。 // Write 方法用於將 p 中的數據寫入到對象的數據流中, // 返回寫入的字節數和遇到的錯誤。 // 如果 p 中的數據全部被寫入,則 err 應該返回 nil。 // 如果 p 中的數據無法被全部寫入,則 err 應該返回相應的錯誤信息。 type Writer interface { Write(p []byte) (n int, err error) } ------------------------------ // Closer 接口包裝了基本的 Close 方法,用於關閉數據讀寫。 // Close 一般用於關閉文件,關閉通道,關閉連接,關閉數據庫等 type Closer interface { Close() error } ------------------------------ // Seeker 接口包裝了基本的 Seek 方法,用於移動數據的讀寫指針。 // Seek 設置下一次讀寫操作的指針位置,每次的讀寫操作都是從指針位置開始的。 // whence 的含義: // 如果 whence 為 0:表示從數據的開頭開始移動指針。 // 如果 whence 為 1:表示從數據的當前指針位置開始移動指針。 // 如果 whence 為 2:表示從數據的尾部開始移動指針。 // offset 是指針移動的偏移量。 // 返回新指針位置和遇到的錯誤。 type Seeker interface { Seek(offset int64, whence int) (ret int64, err error) } ------------------------------ // 下面是這些接口的組合接口 type ReadWriter interface { Reader Writer } type ReadSeeker interface { Reader Seeker } type WriteSeeker interface { Writer Seeker } type ReadWriteSeeker interface { Reader Writer Seeker } type ReadCloser interface { Reader Closer } type WriteCloser interface { Writer Closer } type ReadWriteCloser interface { Reader Writer Closer } ------------------------------ // ReaderFrom 接口包裝了基本的 ReadFrom 方法,用於從 r 中讀取數據存入自身。 // 直到遇到 EOF 或讀取出錯為止,返回讀取的字節數和遇到的錯誤。 type ReaderFrom interface { ReadFrom(r Reader) (n int64, err error) } ------------------------------ // WriterTo 接口包裝了基本的 WriteTo 方法,用於將自身的數據寫入 w 中。 // 直到數據全部寫入完畢或遇到錯誤為止,返回寫入的字節數和遇到的錯誤。 type WriterTo interface { WriteTo(w Writer) (n int64, err error) } ------------------------------ // ReaderAt 接口包裝了基本的 ReadAt 方法,用於將自身的數據寫入 p 中。 // ReadAt 忽略之前的讀寫位置,從起始位置的 off 偏移處開始讀取。 // // 返回寫入的字節數和遇到的錯誤,如果 p 被寫滿,則 err 會返回 nil。如果 p 沒 // 有被寫滿,則會返回一個錯誤信息用於說明為什么沒有寫滿(比如 io.EOF)。在這 // 方面 ReadAt 比 Read 更嚴格。如果 p 被寫滿的同時,自身的數據也剛好被讀完, // 則 err 即可以返回 nil 也可以返回 io.EOF。 // // 即使不能將 p 填滿,ReadAt 在被調用時也可能會使用整個 p 的空間作為緩存空間。 // 如果 ReadAt 自身的數據是從其它地方(比如網絡)獲取數的,那么在寫入 p 的時 // 候,如果沒有把 p 寫滿(比如網絡延時),則 ReadAt 會阻塞,直到獲取更多的數 // 據把 p 寫滿,或者所有數據都獲取完畢,或者遇到讀取錯誤(比如超時)時才返回。 // 在這方面,ReadAt 和 Read 是不同的。 // // 如果 ReadAt 讀取的對象是某個有偏移量的底層數據流時,則 ReadAt 方法既不能影 // 響底層的偏移量,也不應該被底層的偏移量影響。 // // ReadAt 的調用者可以對同一數據流並行執行 ReadAt 方法。 // // ReaderAt 的實現者不應該持有 p。 type ReaderAt interface { ReadAt(p []byte, off int64) (n int, err error) } ------------------------------ // WriterAt 接口包裝了基本的 WriteAt 方法,用於將 p 中的數據寫入自身。 // ReadAt 忽略之前的讀寫位置,從起始位置的 off 偏移處開始寫入。 // // 返回寫入的字節數和遇到的錯誤。如果 p 沒有被讀完,則必須返回一個 err 值來說 // 明為什么沒有讀完。 // // 如果 WriterAt 寫入的對象是某個有偏移量的底層數據流時,則 ReadAt 方法既不能 // 影響底層的偏移量,也不應該被底層的偏移量影響。 // // WriterAt 的調用者可以對同一數據流的不同區段並行執行 WriteAt 方法。 // // WriterAt 的實現者不應該持有 p。 type WriterAt interface { WriteAt(p []byte, off int64) (n int, err error) } ------------------------------ // ByteReader 接口包裝了基本的 ReadByte 方法,用於從自身讀出一個字節。 // 返回讀出的字節和遇到的錯誤。 type ByteReader interface { ReadByte() (c byte, err error) } ------------------------------ // ByteScanner 在 ByteReader 的基礎上增加了一個 UnreadByte 方法,用於撤消最后 // 一次的 ReadByte 操作,以便下次的 ReadByte 操作可以讀出與前一次一樣的數據。 // UnreadByte 之前必須是 ReadByte 才能撤消成功,否則可能會返回一個錯誤信息(根 // 據不同的需求,UnreadByte 也可能返回 nil,允許隨意調用 UnreadByte,但只有最 // 后一次的 ReadByte 可以被撤銷,其它 UnreadByte 不執行任何操作)。 type ByteScanner interface { ByteReader UnreadByte() error } ------------------------------ // ByteWriter 接口包裝了基本的 WriteByte 方法,用於將一個字節寫入自身 // 返回遇到的錯誤 type ByteWriter interface { WriteByte(c byte) error } ------------------------------ // RuneReader 接口包裝了基本的 ReadRune 方法,用於從自身讀取一個 UTF-8 編碼的 // 字符到 r 中。 // 返回讀取的字符、字符的編碼長度和遇到的錯誤。 type RuneReader interface { ReadRune() (r rune, size int, err error) } ------------------------------ // RuneScanner 在 RuneReader 的基礎上增加了一個 UnreadRune 方法,用於撤消最后 // 一次的 ReadRune 操作,以便下次的 ReadRune 操作可以讀出與前一次一樣的數據。 // UnreadRune 之前必須是 ReadRune 才能撤消成功,否則可能會返回一個錯誤信息(根 // 據不同的需求,UnreadRune 也可能返回 nil,允許隨意調用 UnreadRune,但只有最 // 后一次的 ReadRune 可以被撤銷,其它 UnreadRune 不執行任何操作)。 type RuneScanner interface { RuneReader UnreadRune() error } ------------------------------ // bytes.NewBuffer 實現了很多基本的接口,可以通過 bytes 包學習接口的實現 func main() { buf := bytes.NewBuffer([]byte("Hello World!")) b := make([]byte, buf.Len()) n, err := buf.Read(b) fmt.Printf("%s %v\n", b[:n], err) // Hello World! <nil> buf.WriteString("ABCDEFG\n") buf.WriteTo(os.Stdout) // ABCDEFG n, err = buf.Write(b) fmt.Printf("%d %s %v\n", n, buf.String(), err) // 12 Hello World! <nil> c, err := buf.ReadByte() fmt.Printf("%c %s %v\n", c, buf.String(), err) // H ello World! <nil> c, err = buf.ReadByte() fmt.Printf("%c %s %v\n", c, buf.String(), err) // e llo World! <nil> err = buf.UnreadByte() fmt.Printf("%s %v\n", buf.String(), err) // ello World! <nil> err = buf.UnreadByte() fmt.Printf("%s %v\n", buf.String(), err) // ello World! bytes.Buffer: UnreadByte: previous operation was not a read } ------------------------------------------------------------ // WriteString 將字符串 s 寫入到 w 中,返回寫入的字節數和遇到的錯誤。 // 如果 w 實現了 WriteString 方法,則優先使用該方法將 s 寫入 w 中。 // 否則,將 s 轉換為 []byte,然后調用 w.Write 方法將數據寫入 w 中。 func WriteString(w Writer, s string) (n int, err error) // ReadAtLeast 從 r 中讀取數據到 buf 中,要求至少讀取 min 個字節。 // 返回讀取的字節數和遇到的錯誤。 // 如果 min 超出了 buf 的容量,則 err 返回 io.ErrShortBuffer,否則: // 1、讀出的數據長度 == 0 ,則 err 返回 EOF。 // 2、讀出的數據長度 < min,則 err 返回 io.ErrUnexpectedEOF。 // 3、讀出的數據長度 >= min,則 err 返回 nil。 func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) // ReadFull 的功能和 ReadAtLeast 一樣,只不過 min = len(buf) func ReadFull(r Reader, buf []byte) (n int, err error) // CopyN 從 src 中復制 n 個字節的數據到 dst 中,返回復制的字節數和遇到的錯誤。 // 只有當 written = n 時,err 才返回 nil。 // 如果 dst 實現了 ReadFrom 方法,則優先調用該方法執行復制操作。 func CopyN(dst Writer, src Reader, n int64) (written int64, err error) // Copy 從 src 中復制數據到 dst 中,直到所有數據都復制完畢,返回復制的字節數和 // 遇到的錯誤。如果復制過程成功結束,則 err 返回 nil,而不是 EOF,因為 Copy 的 // 定義為“直到所有數據都復制完畢”,所以不會將 EOF 視為錯誤返回。 // 如果 src 實現了 WriteTo 方法,則調用 src.WriteTo(dst) 復制數據,否則 // 如果 dst 實現了 ReadeFrom 方法,則調用 dst.ReadeFrom(src) 復制數據 func Copy(dst Writer, src Reader) (written int64, err error) // CopyBuffer 相當於 Copy,只不 Copy 在執行的過程中會創建一個臨時的緩沖區來中 // 轉數據,而 CopyBuffer 則可以單獨提供一個緩沖區,讓多個復制操作共用同一個緩 // 沖區,避免每次復制操作都創建新的緩沖區。如果 buf == nil,則 CopyBuffer 會 // 自動創建緩沖區。 func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) ------------------------------ // 示例:WriteString、ReadAtLeast、ReadFull func main() { io.WriteString(os.Stdout, "Hello World!\n") // Hello World! r := strings.NewReader("Hello World!") b := make([]byte, 15) n, err := io.ReadAtLeast(r, b, 20) fmt.Printf("%q %d %v\n", b[:n], n, err) // "" 0 short buffer r.Seek(0, 0) b = make([]byte, 15) n, err = io.ReadFull(r, b) fmt.Printf("%q %d %v\n", b[:n], n, err) // "Hello World!" 12 unexpected EOF } ------------------------------ // 示例:CopyN、Copy、CopyBuffer func main() { r := strings.NewReader("Hello World!") buf := make([]byte, 32) n, err := io.CopyN(os.Stdout, r, 5) // Hello fmt.Printf("\n%d %v\n\n", n, err) // 5 <nil> r.Seek(0, 0) n, err = io.Copy(os.Stdout, r) // Hello World! fmt.Printf("\n%d %v\n\n", n, err) // 12 <nil> r.Seek(0, 0) r2 := strings.NewReader("ABCDEFG") r3 := strings.NewReader("abcdefg") n, err = io.CopyBuffer(os.Stdout, r, buf) // Hello World! fmt.Printf("\n%d %v\n", n, err) // 12 <nil> n, err = io.CopyBuffer(os.Stdout, r2, buf) // ABCDEFG fmt.Printf("\n%d %v\n", n, err) // 7 <nil> n, err = io.CopyBuffer(os.Stdout, r3, buf) // abcdefg fmt.Printf("\n%d %v\n", n, err) // 7 <nil> } ------------------------------------------------------------ // LimitReader 對 r 進行封裝,使其最多只能讀取 n 個字節的數據。相當於對 r 做了 // 一個切片 r[:n] 返回。底層實現是一個 *LimitedReader(只有一個 Read 方法)。 func LimitReader(r Reader, n int64) Reader // MultiReader 將多個 Reader 封裝成一個單獨的 Reader,多個 Reader 會按順序讀 // 取,當多個 Reader 都返回 EOF 之后,單獨的 Reader 才返回 EOF,否則返回讀取 // 過程中遇到的任何錯誤。 func MultiReader(readers ...Reader) Reader // MultiReader 將向自身寫入的數據同步寫入到所有 writers 中。 func MultiWriter(writers ...Writer) Writer // TeeReader 對 r 進行封裝,使 r 在讀取數據的同時,自動向 w 中寫入數據。 // 它是一個無緩沖的 Reader,所以對 w 的寫入操作必須在 r 的 Read 操作結束 // 之前完成。所有寫入時遇到的錯誤都會被作為 Read 方法的 err 返回。 func TeeReader(r Reader, w Writer) Reader ------------------------------ // 示例 LimitReader func main() { r := strings.NewReader("Hello World!") lr := io.LimitReader(r, 5) n, err := io.Copy(os.Stdout, lr) // Hello fmt.Printf("\n%d %v\n", n, err) // 5 <nil> } ------------------------------ // 示例 MultiReader func main() { r1 := strings.NewReader("Hello World!") r2 := strings.NewReader("ABCDEFG") r3 := strings.NewReader("abcdefg") b := make([]byte, 15) mr := io.MultiReader(r1, r2, r3) for n, err := 0, error(nil); err == nil; { n, err = mr.Read(b) fmt.Printf("%q\n", b[:n]) } // "Hello World!" // "ABCDEFG" // "abcdefg" // "" r1.Seek(0, 0) r2.Seek(0, 0) r3.Seek(0, 0) mr = io.MultiReader(r1, r2, r3) io.Copy(os.Stdout, mr) // Hello World!ABCDEFGabcdefg } ------------------------------ // 示例 MultiWriter func main() { r := strings.NewReader("Hello World!\n") mw := io.MultiWriter(os.Stdout, os.Stdout, os.Stdout) r.WriteTo(mw) // Hello World! // Hello World! // Hello World! } // 示例 TeeReader func main() { r := strings.NewReader("Hello World!") b := make([]byte, 15) tr := io.TeeReader(r, os.Stdout) n, err := tr.Read(b) // Hello World! fmt.Printf("\n%s %v\n", b[:n], err) // Hello World! <nil> } ------------------------------------------------------------ // NewSectionReader 對 r 進行封裝,使其只能從 off 位置開始讀取,最多只能讀取 n // 個字節的的數據。相當於對 r 做了一個切片 r[off:off+n] 返回。 // 底層實現是一個 *SectionReader。 func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader // SectionReader 實現了如下接口: // io.Reader // io.ReaderAt // io.Seeker // Size 返回允許讀取部分的大小(即切片的長度 n) func (s *SectionReader) Size() ------------------------------ // 示例 SectionReader func main() { r := strings.NewReader("Hello World!") sr := io.NewSectionReader(r, 6, 5) n, err := io.Copy(os.Stdout, sr) // World fmt.Printf("\n%d %d %v\n", sr.Size(), n, err) // 5 5 <nil> } ------------------------------------------------------------ // Pipe 在內存中創建一個同步管道,用於不同區域的代碼之間相互傳遞數據。 // 返回的 *PipeReader 用於從管道中讀取數據,*PipeWriter 用於向管道中寫入數據。 // 管道沒有緩沖區,讀寫操作可能會被阻塞。可以安全的對管道進行並行的讀、寫或關閉 // 操作,讀寫操作會依次執行,Close 會在被阻塞的 I/O 操作結束之后完成。 func Pipe() (*PipeReader, *PipeWriter) // 從管道中讀取數據,如果管道被關閉,則會返會一個錯誤信息: // 1、如果寫入端通過 CloseWithError 方法關閉了管道,則返回關閉時傳入的錯誤信息。 // 2、如果寫入端通過 Close 方法關閉了管道,則返回 io.EOF。 // 3、如果是讀取端關閉了管道,則返回 io.ErrClosedPipe。 func (r *PipeReader) Read(data []byte) (n int, err error) // 關閉管道 func (r *PipeReader) Close() error // 關閉管道並傳入錯誤信息。 func (r *PipeReader) CloseWithError(err error) error // 向管道中寫入數據,如果管道被關閉,則會返會一個錯誤信息: // 1、如果讀取端通過 CloseWithError 方法關閉了管道,則返回關閉時傳入的錯誤信息。 // 2、如果讀取端通過 Close 方法關閉了管道,則返回 io.ErrClosedPipe。 // 3、如果是寫入端關閉了管道,則返回 io.ErrClosedPipe。 func (w *PipeWriter) Write(data []byte) (n int, err error) // 關閉管道 func (w *PipeWriter) Close() error // 關閉管道並傳入錯誤信息。 func (w *PipeWriter) CloseWithError(err error) error ------------------------------ // 示例:管道(讀取端關閉) func main() { r, w := io.Pipe() // 啟用一個例程進行讀取 go func() { buf := make([]byte, 5) for n, err := 0, error(nil); err == nil; { n, err = r.Read(buf) r.CloseWithError(errors.New("管道被讀取端關閉")) fmt.Printf("讀取:%d, %v, %s\n", n, err, buf[:n]) } }() // 主例程進行寫入 n, err := w.Write([]byte("Hello World !")) fmt.Printf("寫入:%d, %v\n", n, err) } ------------------------------ // 示例:管道(寫入端關閉) func main() { r, w := io.Pipe() // 啟用一個例程進行讀取 go func() { buf := make([]byte, 5) for n, err := 0, error(nil); err == nil; { n, err = r.Read(buf) fmt.Printf("讀取:%d, %v, %s\n", n, err, buf[:n]) } }() // 主例程進行寫入 n, err := w.Write([]byte("Hello World !")) fmt.Printf("寫入:%d, %v\n", n, err) w.CloseWithError(errors.New("管道被寫入端關閉")) n, err = w.Write([]byte("Hello World !")) fmt.Printf("寫入:%d, %v\n", n, err) time.Sleep(time.Second * 1) } ------------------------------------------------------------