Go
原生的pkg
中有一些核心的interface
,其中io.Reader/Writer
是比較經常使用的接口。不少原生的結構都圍繞這個系列的接口展開,在實際的開發過程當中,你會發現經過這個接口能夠在多種不一樣的io類型之間進行過渡和轉化。本文結合實際場景來總結一番。網絡
圍繞io.Reader/Writer
,有幾個經常使用的實現:ui
[]byte
抽象成Reader[]byte
抽象成Reader和Writer這些實現對於初學者來講其實比較難去記憶,在遇到實際問題的時候更是一臉蒙圈,不知如何是好。下面用實際的場景來舉例編碼
encoding/base64
包中:spa
func NewEncoder(enc *Encoding, w io.Writer) io.WriteCloser
這個用來作base64
編碼,可是仔細觀察發現,它須要一個io.Writer做爲輸出目標,並用返回的WriteCloser
的Write方法將結果寫入目標,下面是Go官方文檔的例子code
input := []byte("foo\x00bar") encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout) encoder.Write(input)
這個例子是將結果寫入到Stdout
,若是咱們但願獲得一個字符串呢?觀察上面的圖,否則發現能夠用bytes.Buffer做爲目標io.Writer
:接口
input := []byte("foo\x00bar") buffer := new(bytes.Buffer) encoder := base64.NewEncoder(base64.StdEncoding, buffer) encoder.Write(input) fmt.Println(string(buffer.Bytes())
這種場景常常用在基於字節的協議上,好比有一個具備固定長度的結構:開發
type Protocol struct { Version uint8 BodyLen uint16 Reserved [2]byte Unit uint8 Value uint32 }
經過一個[]byte
來反序列化獲得這個Protocol
,一種思路是遍歷這個[]byte
,而後逐一賦值。其實在encoding/binary
包中有個方便的方法:rem
func Read(r io.Reader, order ByteOrder, data interface{}) error
這個方法從一個io.Reader
中讀取字節,並已order
指定的端模式,來給填充data
(data須要是fixed-sized的結構或者類型)。要用到這個方法首先要有一個io.Reader
,從上面的圖中不難發現,咱們能夠這麼寫:文檔
var p Protocol var bin []byte //... binary.Read(bytes.NewReader(bin), binary.LittleEndian, &p)
換句話說,咱們將一個[]byte
轉成了一個io.Reader
。字符串
反過來,咱們須要將Protocol
序列化獲得[]byte
,使用encoding/binary
包中有個對應的Write
方法:
func Write(w io.Writer, order ByteOrder, data interface{}) error
經過將[]byte
轉成一個io.Writer
便可:
var p Protocol buffer := new(bytes.Buffer) //... binary.Writer(buffer, binary.LittleEndian, p) bin := buffer.Bytes()
好比對於常見的基於文本行的HTTP
協議的讀取,咱們須要將一個流按照行來讀取。本質上,咱們須要一個基於緩衝的讀寫機制(讀一些到緩衝,而後遍歷緩衝中咱們關心的字節或字符)。在Go中有一個bufio
的包能夠實現帶緩衝的讀寫:
func NewReader(rd io.Reader) *Reader func (b *Reader) ReadString(delim byte) (string, error)
這個ReadString方法從io.Reader
中讀取字符串,直到delim
,就返回delim
和以前的字符串。若是將delim
設置爲\n
,至關於按行來讀取了:
var conn net.Conn //... reader := NewReader(conn) for { line, err := reader.ReadString([]byte('\n')) //... }
a := "Hello, playground" fmt.Println([]byte(a))
等價於
a := "Hello, playground" buf := new(bytes.Buffer) buf.ReadFrom(strings.NewReader(a)) fmt.Println(buf.Bytes())