先說一下接口,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 返回允許讀取部分的大?。辞衅拈L度 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 用于向管道中寫入數據。
// 管道沒有緩沖區,讀寫操作可能會被阻塞??梢园踩膶艿肋M行并行的讀、寫或關閉
// 操作,讀寫操作會依次執行,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)
}