golang io中io.go解讀


根據golang io源碼包解讀io.go文件。

1. 整體大綱

分別從接口,函數以及結構體去解讀golang io 包中io.go文件。


2. 接口

在源代碼中,對於 IO 流,定義了四個基本操作原語,分別用 Reader,Writer,Closer,Seeker 接口表達二進制流讀、寫、關閉、尋址等操作。根據其中的性質來區分,將分為讀,寫,關閉以及尋址等解讀。

詳細實現參考: bytes.Buffer

Reader

type Reader interface {
	Read(p []byte) (n int, err error)
}

Reader 接口包裝了基本的 Read 方法,用於輸出自身(實現者)的數據到p。Read 方法用於將對象的數據流讀入到 p 中,返回讀取的字節數和遇到的錯誤。實現者不能包含p。

  • 在沒有遇到讀取錯誤的情況下:
    • 如果讀到了數據(n > 0),則應該返回 n,nil。
    • 如果數據被讀空,沒有數據可讀(n == 0),則應該返回 0,EOF[1];
  • 遇到讀取錯誤,則 err 應該返回相應的錯誤信息(如果在讀取過程中發了錯誤即n>0,那么要考慮處理這種情況,返回錯誤為ErrUnexpectedEOF[2]);
  • 返回0,nil,那么代表什么都沒有發生。
	buf := bytes.NewBuffer([]byte("Hello World!"))
	b := make([]byte, buf.Len())

	n, err := buf.Read(b)
	fmt.Printf("%s   %v\n", b[:n], err) // output: Hello World!   <nil>
ReaderFrom
type ReaderFrom interface {
	ReadFrom(r Reader) (n int64, err error)
}

ReaderFrom 接口包裝了基本的 ReadFrom 方法,用於從 r 中讀取數據存入自身(即實現者本身帶有p)。 直到遇到 EOF 或讀取出錯為止,返回讀取的字節數和遇到的錯誤。

	buf := bytes.NewBuffer([]byte("Hello World!"))
	dst := bytes.Buffer{}

	dst.ReadFrom(buf)
	dst.WriteTo(os.Stdout) // output: Hello World!

ReaderAt

type ReaderAt interface {
	ReadAt(p []byte, off int64) (n int, 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。

ByteReader

type ByteReader interface {
	ReadByte() (byte, error)
}

ByteReader 接口包裝了基本的 ReadByte 方法,用於從自身讀出一個字節。

返回讀出的字節和遇到的錯誤。如果返回錯誤,那么沒有任何輸入byte被消費,所返回的byte也是無效的。

	buf := bytes.NewBuffer([]byte("Hello World!"))

	c, err := buf.ReadByte()
	fmt.Printf("%c   %s   %v\n", c, buf.String(), err) // output: H   ello World!   <nil>
ByteScanner
type ByteScanner interface {
	ByteReader
	UnreadByte() error
}

ByteScanner 在 ByteReader 的基礎上增加了一個 UnreadByte 方法,用於撤消最后一次的 ReadByte 操作,以便下次的 ReadByte 操作可以讀出與前一次一樣的數據。

UnreadByte 之前必須是 ReadByte 才能撤消成功,否則可能會返回一個錯誤信息(根 據不同的需求,UnreadByte 也可能返回 nil,允許隨意調用 UnreadByte,但只有最后一次的 ReadByte 可以被撤銷,其它 UnreadByte 不執行任何操作)。

	buf := bytes.NewBuffer([]byte("Hello World!"))

	c, err := buf.ReadByte()
	fmt.Printf("%c   %s   %v\n", c, buf.String(), err)// output: H   ello World!   <nil>

	err = buf.UnreadByte()
	fmt.Printf("%s   %v\n", buf.String(), err)//output: Hello World!   <nil>

RuneReader

type RuneReader interface {
	ReadRune() (r rune, size int, err error)
}

RuneReader 接口包裝了基本的 ReadRune 方法,用於從自身讀取一個 UTF-8 編碼的字符到 r 中。

返回讀取的字符、字符的編碼長度和遇到的錯誤。

	buf := bytes.NewBuffer([]byte("愛Hello World!"))

	c,s, err := buf.ReadRune()
	fmt.Printf("%c  %d  %s   %v\n", c,s, buf.String(), err) // output: 愛  3  Hello World!   <nil>
RuneScanner
type RuneScanner interface {
	RuneReader
	UnreadRune() error
}

RuneScanner 在 RuneReader 的基礎上增加了一個 UnreadRune 方法,用於撤消最后一次的 ReadRune 操作,以便下次的 ReadRune 操作可以讀出與前一次一樣的數據。UnreadRune(操作) 之前必須是 ReadRune(操作) 才能撤消成功,否則可能會返回一個錯誤信息(根據不同的需求,UnreadRune 也可能返回 nil,允許隨意調用 UnreadRune,但只有最后一次的 ReadRune 可以被撤銷,其它 UnreadRune 不執行任何操作)。

	buf := bytes.NewBuffer([]byte("愛Hello World!"))

	c,s, err := buf.ReadRune()
	fmt.Printf("%c  %d  %s   %v\n", c,s, buf.String(), err) // output: 愛  3  Hello World!   <nil>

	err = buf.UnreadRune()
	fmt.Printf("%c  %d  %s   %v\n", c,s, buf.String(), err)// output: 愛  3  愛Hello World!   <nil>

Writer

type Writer interface {
	Write(p []byte) (n int, err error)
}

Writer 接口包裝了基本的 Write 方法,用於將數據存入自身。Write 方法用於將 p 中的數據寫入到對象的數據流中,返回寫入的字節數和遇到的錯誤。

  • 如果 p 中的數據全部被寫入,則 err 應該返回 nil。
  • 如果 p 中的數據無法被全部寫入,則 err 應該返回相應的錯誤信息。
WriterTo
type WriterTo interface {
	WriteTo(w Writer) (n int64, err error)
}

WriterTo 接口包裝了基本的 WriteTo 方法,用於將自身的數據寫入 w 中。

直到數據全部寫入完畢或遇到錯誤為止,返回寫入的字節數和遇到的錯誤。

WriterAt

type WriterAt interface {
	WriteAt(p []byte, off int64) (n int, err error)
}

WriterAt 接口包裝了基本的 WriteAt 方法,用於將 p 中的數據寫入自身。

ReadAt 忽略之前的讀寫位置,從起始位置的 off 偏移處開始寫入。

返回寫入的字節數和遇到的錯誤。如果 p 沒有被讀完,則必須返回一個 err 值來說明為什么沒有讀完。

如果 WriterAt 寫入的對象是某個有偏移量的底層數據流時,則 ReadAt 方法既不能影響底層的偏移量,也不應該被底層的偏移量影響。

WriterAt 的調用者可以對同一數據流的不同區段並行執行 WriteAt 方法。WriterAt 的實現者不應該持有 p。

ByteWriter

type ByteWriter interface {
	WriteByte(c byte) error
}

ByteWriter 接口包裝了基本的 WriteByte 方法,用於將一個字節寫入自身。

返回遇到的錯誤

關閉

Closer

type Closer interface {
	Close() error
}

Closer 接口包裝了基本的 Close 方法,用於關閉數據讀寫。

Close 一般用於關閉文件,關閉通道,關閉連接,關閉數據庫等

尋址

Seeker

type Seeker interface {
	Seek(offset int64, whence int) (int64, error)
}

Seeker 接口包裝了基本的 Seek 方法,用於移動數據的讀寫指針。

Seek 設置下一次讀寫操作的指針位置,每次的讀寫操作都是從指針位置開始的。

whence 的含義:

  • 如果 whence 為 0:表示從數據的開頭開始移動指針。
  • 如果 whence 為 1:表示從數據的當前指針位置開始移動指針。
  • 如果 whence 為 2:表示從數據的尾部開始移動指針。

offset 是指針移動的偏移量。返回新指針位置和遇到的錯誤。

	r := strings.NewReader("Hello World!")

	n, err := io.CopyN(os.Stdout, r, 5) // output: Hello
	fmt.Printf("\n%d   %v\n\n", n, err) // output: 5   <nil>

	r.Seek(0, 0)
	n, err = io.Copy(os.Stdout, r)      // output: Hello World!
	fmt.Printf("\n%d   %v\n\n", n, err) // output: 12   <nil>

3. 函數

ReadFull

func ReadFull(r Reader, buf []byte) (n int, err error) {
	return ReadAtLeast(r, buf, len(buf))
}

這個函數可以把對象 r 中的數據讀出來,然后存入一個緩沖區 buf 中,以便其它代碼可以處理 buf 中的數據。

如果沒有數據讀取,那么久返回拷貝的字節數和一個錯誤。

  • 返回n,EOF代表沒有字節可以讀取了
  • 返回ErrUnexpectedEOF,如果在讀取數據的過程中發生了err
  • 返回 n == len(buf) 或者 err == nil,代表err不存在
// 定義一個 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
}

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) //output:  HELLO WORLD!
	fmt.Println(n, err)     //output:  12 <nil>
}

ReadAtLeast

func ReadFull(r Reader, buf []byte) (n int, err error) {
	return ReadAtLeast(r, buf, len(buf))
}

ReadAtLeast 從 r 中讀取數據到 buf 中,要求至少讀取 min 個字節。
返回讀取的字節數和遇到的錯誤。
如果 min 超出了 buf 的容量,則 err 返回 io.ErrShortBuffer,否則:

  • 讀出的數據長度 == 0 ,則 err 返回 EOF[1:1]
  • 讀出的數據長度 < min,則 err 返回 io.ErrUnexpectedEOF[2:1]
  • 讀出的數據長度 >= min,則 err 返回 nil。
	r := strings.NewReader("Hello World!") // 數據長度為12
	b := make([]byte, 15)

	n, err := io.ReadAtLeast(r, b, 12) // 要求讀取至少12個字節
	fmt.Printf("%q   %d   %v\n", b[:n], n, err) // output: "Hello World!"   12   <nil>

LimitReader

func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} }

LimitReader 對 r 進行封裝,使其最多只能讀取 n 個字節的數據。相當於對 r 做了一個切片 r[:n] 返回。底層實現是一個 *LimitedReader(只有一個 Read 方法)。

	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) //// output: 5   <nil>

MultiReader

func MultiReader(readers ...Reader) Reader {
	r := make([]Reader, len(readers))
	copy(r, readers)
	return &multiReader{r}
}

MultiReader 將多個 Reader 封裝成一個單獨的 Reader,多個 Reader 會按順序讀取,當多個 Reader 都返回 EOF 之后,單獨的 Reader 才返回 EOF,否則返回讀取過程中遇到的任何錯誤。

	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) // output: Hello World!ABCDEFGabcdefg

TeeReader

func TeeReader(r Reader, w Writer) Reader {
	return &teeReader{r, w}
}

TeeReader 對 r 進行封裝,使 r 在讀取數據的同時,自動向 w 中寫入數據。它是一個無緩沖的 Reader,所以對 w 的寫入操作必須在 r 的 Read 操作結束之前完成。所有寫入時遇到的錯誤都會被作為 Read 方法的 err 返回。

	r := strings.NewReader("Hello World!")
	b := make([]byte, 15)
	tr := io.TeeReader(r, os.Stdout)  // 會在屏幕輸出

	n, err := tr.Read(b)                  // output: Hello World!
	fmt.Printf("\n%s   %v\n", b[:n], err) //output: Hello World!   <nil>

WriteString

func WriteString(w Writer, s string) (n int, err error) {
	if sw, ok := w.(StringWriter); ok {
		return sw.WriteString(s)
	}
	return w.Write([]byte(s))
}

WriteString 將字符串 s 寫入到 w 中,返回寫入的字節數和遇到的錯誤。
如果 w 實現了 WriteString 方法,則優先使用該方法將 s 寫入 w 中。否則,將 s 轉換為 []byte,然后調用 w.Write 方法將數據寫入 w 中。

	io.WriteString(os.Stdout, "Hello World!\n") // output:  Hello World!

MultiWriter

func MultiWriter(writers ...Writer) Writer {
	allWriters := make([]Writer, 0, len(writers))
	for _, w := range writers {
		if mw, ok := w.(*multiWriter); ok {
			allWriters = append(allWriters, mw.writers...)
		} else {
			allWriters = append(allWriters, w)
		}
	}
	return &multiWriter{allWriters}
}

MultiReader 將向自身寫入的數據同步寫入到所有 writers 中。

	r := strings.NewReader("Hello World!\n")
	mw := io.MultiWriter(os.Stdout, os.Stdout, os.Stdout)

	r.WriteTo(mw) 
	// output: Hello World!
	// output: Hello World!
	// output: Hello World!

復制

CopyN

func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
	written, err = Copy(dst, LimitReader(src, n))
	if written == n {
		return n, nil
	}
	if written < n && err == nil {
		// src stopped early; must have been EOF.
		err = EOF
	}
	return
}

CopyN 從 src 中復制 n 個字節的數據到 dst 中,返回復制的字節數和遇到的錯誤。

  • 只有當 written = n 時,err 才返回 nil。

如果 dst 實現了 ReadFrom 方法,則優先調用該方法執行復制操作。

	r := strings.NewReader("Hello World!")

	n, err := io.CopyN(os.Stdout, r, 5) // output:Hello
	fmt.Printf("\n%d   %v\n\n", n, err) // output:5   <nil>

CopyBuffer

func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
	if buf != nil && len(buf) == 0 {
		panic("empty buffer in io.CopyBuffer")
	}
	return copyBuffer(dst, src, buf)
}

CopyBuffer 相當於 Copy,只不過Copy 在執行的過程中會創建一個臨時的緩沖區來中轉數據,而 CopyBuffer 則可以單獨提供一個緩沖區,讓多個復制操作共用同一個緩沖區,避免每次復制操作都創建新的緩沖區。如果 buf == nil,則 CopyBuffer 會自動創建緩沖區。

	r := strings.NewReader("Hello World!")
	buf := make([]byte, 32)

	n, err := io.CopyBuffer(os.Stdout, r, buf) // output: Hello World!
	fmt.Printf("\n%d   %v\n", n, err)  // output: 12   <nil>

Copy


func Copy(dst Writer, src Reader) (written int64, err error) {
	return copyBuffer(dst, src, nil)
}

Copy 從 src 中復制數據到 dst 中,直到所有數據都復制完畢,返回復制的字節數和遇到的錯誤。

如果復制過程成功結束,則 err 返回 nil,而不是 EOF,因為 Copy 的定義為“直到所有數據都復制完畢”,所以不會將 EOF 視為錯誤返回。

如果 src 實現了 WriteTo 方法,則調用 src.WriteTo(dst) 復制數據,否則如果 dst 實現了 ReadeFrom 方法,則調用 dst.ReadeFrom(src) 復制數據。

	r := strings.NewReader("Hello World!")

	n, err := io.Copy(os.Stdout, r)      // output: Hello World!
	fmt.Printf("\n%d   %v\n\n", n, err) // output:  12   <nil>

4. 結構體

SectionReader

type SectionReader struct {
	r     ReaderAt
	base  int64
	off   int64
	limit int64
}

實現了 Read, Seek, and ReadAt 接口

NewSectionReader

func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
	return &SectionReader{r, off, off, off + n}
}

NewSectionReader 對 r 進行封裝,使其只能從 off 位置開始讀取,最多只能讀取 n個字節的的數據。相當於對 r 做了一個切片 r[off:off+n] 返回。底層實現是一個 *SectionReader。

Size

func (s *SectionReader) Size() int64 { return s.limit - s.base }

Size 返回允許讀取部分的大小(即切片的長度 n)

使用示例

	r := strings.NewReader("Hello World!")
	sr := io.NewSectionReader(r, 6, 5)

	n, err := io.Copy(os.Stdout, sr)                  // output: World
	fmt.Printf("\n%d   %d   %v\n", sr.Size(), n, err) // output: 5   5   <nil>

LimitedReader

type LimitedReader struct {
	R Reader // underlying reader
	N int64  // max bytes remaining
}

實現Read接口

使用示例

	r := strings.NewReader("Hello World!")
	sr := io.LimitedReader{r,2}

	buf := make ([]byte,12)

	n, err := sr.Read(buf)
	fmt.Printf("\n%d  %s   %v\n", n,buf[:n], err) // output: 2  He   <nil>

teeReader

type teeReader struct {
	r Reader
	w Writer
}

結合TeeReader函數使用


5. 備注


  1. EOF is the error returned by Read when no more input is available.Functions should return EOF only to signal a graceful end of input.If the EOF occurs unexpectedly in a structured data stream,the appropriate error is either ErrUnexpectedEOF or some other error ↩︎ ↩︎

  2. ErrUnexpectedEOF means that EOF was encountered in the middle of reading a fixed-size block or data structure. ↩︎ ↩︎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM