Golang學習 - io 包

------------------------------------------------------------

  先說一下接口,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)
}

------------------------------------------------------------



相關文章
相關標籤/搜索