------------------------------------------------------------ 先說一下接口,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 中,返回讀取的字節數和遇到的錯誤。 // 在沒有遇到讀取錯誤的狀況下: // 一、若是讀到了數據(n > 0),則 err 應該返回 nil。 // 二、若是數據被讀空,沒有數據可讀(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,不然: // 一、讀出的數據長度 == 0 ,則 err 返回 EOF。 // 二、讀出的數據長度 < min,則 err 返回 io.ErrUnexpectedEOF。 // 三、讀出的數據長度 >= 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) // 從管道中讀取數據,若是管道被關閉,則會返會一個錯誤信息: // 一、若是寫入端經過 CloseWithError 方法關閉了管道,則返回關閉時傳入的錯誤信息。 // 二、若是寫入端經過 Close 方法關閉了管道,則返回 io.EOF。 // 三、若是是讀取端關閉了管道,則返回 io.ErrClosedPipe。 func (r *PipeReader) Read(data []byte) (n int, err error) // 關閉管道 func (r *PipeReader) Close() error // 關閉管道並傳入錯誤信息。 func (r *PipeReader) CloseWithError(err error) error // 向管道中寫入數據,若是管道被關閉,則會返會一個錯誤信息: // 一、若是讀取端經過 CloseWithError 方法關閉了管道,則返回關閉時傳入的錯誤信息。 // 二、若是讀取端經過 Close 方法關閉了管道,則返回 io.ErrClosedPipe。 // 三、若是是寫入端關閉了管道,則返回 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) } ------------------------------------------------------------