Go編程技巧--io.Reader/Writer

Go原生的pkg中有一些核心的interface,其中io.Reader/Writer是比較常用的接口。很多原生的結(jié)構(gòu)都圍繞這個(gè)系列的接口展開(kāi),在實(shí)際的開(kāi)發(fā)過(guò)程中,你會(huì)發(fā)現(xiàn)通過(guò)這個(gè)接口可以在多種不同的io類型之間進(jìn)行過(guò)渡和轉(zhuǎn)化。本文結(jié)合實(shí)際場(chǎng)景來(lái)總結(jié)一番。

總覽

image.png

圍繞io.Reader/Writer,有幾個(gè)常用的實(shí)現(xiàn):

  • net.Conn, os.Stdin, os.File: 網(wǎng)絡(luò)、標(biāo)準(zhǔn)輸入輸出、文件的流讀取
  • strings.Reader: 把字符串抽象成Reader
  • bytes.Reader: 把[]byte抽象成Reader
  • bytes.Buffer: 把[]byte抽象成Reader和Writer
  • bufio.Reader/Writer: 抽象成帶緩沖的流讀?。ū热绨葱凶x寫(xiě))

這些實(shí)現(xiàn)對(duì)于初學(xué)者來(lái)說(shuō)其實(shí)比較難去記憶,在遇到實(shí)際問(wèn)題的時(shí)候更是一臉蒙圈,不知如何是好。下面用實(shí)際的場(chǎng)景來(lái)舉例

場(chǎng)景舉例

0. base64編碼成字符串

encoding/base64包中:

func NewEncoder(enc *Encoding, w io.Writer) io.WriteCloser

這個(gè)用來(lái)做base64編碼,但是仔細(xì)觀察發(fā)現(xiàn),它需要一個(gè)io.Writer作為輸出目標(biāo),并用返回的WriteCloser的Write方法將結(jié)果寫(xiě)入目標(biāo),下面是Go官方文檔的例子

input := []byte("foo\x00bar")
encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout)
encoder.Write(input)

這個(gè)例子是將結(jié)果寫(xiě)入到Stdout,如果我們希望得到一個(gè)字符串呢?觀察上面的圖,不然發(fā)現(xiàn)可以用bytes.Buffer作為目標(biāo)io.Writer

input := []byte("foo\x00bar")
buffer := new(bytes.Buffer)
encoder := base64.NewEncoder(base64.StdEncoding, buffer)
encoder.Write(input)
fmt.Println(string(buffer.Bytes())

1. []byte和struct之間正反序列化

這種場(chǎng)景經(jīng)常用在基于字節(jié)的協(xié)議上,比如有一個(gè)具有固定長(zhǎng)度的結(jié)構(gòu):

type Protocol struct {
    Version     uint8
    BodyLen     uint16
    Reserved    [2]byte
    Unit        uint8
    Value       uint32
}

通過(guò)一個(gè)[]byte來(lái)反序列化得到這個(gè)Protocol,一種思路是遍歷這個(gè)[]byte,然后逐一賦值。其實(shí)在encoding/binary包中有個(gè)方便的方法:

func Read(r io.Reader, order ByteOrder, data interface{}) error

這個(gè)方法從一個(gè)io.Reader中讀取字節(jié),并已order指定的端模式,來(lái)給填充data(data需要是fixed-sized的結(jié)構(gòu)或者類型)。要用到這個(gè)方法首先要有一個(gè)io.Reader,從上面的圖中不難發(fā)現(xiàn),我們可以這么寫(xiě):

var p Protocol
var bin []byte
//...
binary.Read(bytes.NewReader(bin), binary.LittleEndian, &p)

換句話說(shuō),我們將一個(gè)[]byte轉(zhuǎn)成了一個(gè)io.Reader。

反過(guò)來(lái),我們需要將Protocol序列化得到[]byte,使用encoding/binary包中有個(gè)對(duì)應(yīng)的Write方法:

func Write(w io.Writer, order ByteOrder, data interface{}) error

通過(guò)將[]byte轉(zhuǎn)成一個(gè)io.Writer即可:

var p Protocol
buffer := new(bytes.Buffer)
//...
binary.Writer(buffer, binary.LittleEndian, p)
bin := buffer.Bytes()

2. 從流中按行讀取

比如對(duì)于常見(jiàn)的基于文本行的HTTP協(xié)議的讀取,我們需要將一個(gè)流按照行來(lái)讀取。本質(zhì)上,我們需要一個(gè)基于緩沖的讀寫(xiě)機(jī)制(讀一些到緩沖,然后遍歷緩沖中我們關(guān)心的字節(jié)或字符)。在Go中有一個(gè)bufio的包可以實(shí)現(xiàn)帶緩沖的讀寫(xiě):

func NewReader(rd io.Reader) *Reader
func (b *Reader) ReadString(delim byte) (string, error)

這個(gè)ReadString方法從io.Reader中讀取字符串,直到delim,就返回delim和之前的字符串。如果將delim設(shè)置為\n,相當(dāng)于按行來(lái)讀取了:

var conn net.Conn
//...
reader := NewReader(conn)
for {
    line, err := reader.ReadString([]byte('\n'))
    //...
}

花式技(zuo)巧(si)

string轉(zhuǎn)[]byte

a := "Hello, playground"
fmt.Println([]byte(a))

等價(jià)于

a := "Hello, playground"
buf := new(bytes.Buffer)
buf.ReadFrom(strings.NewReader(a))
fmt.Println(buf.Bytes())
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • tags:io categories:總結(jié) date: 2017-03-28 22:49:50 不僅僅在JAVA領(lǐng)...
    行徑行閱讀 2,304評(píng)論 0 3
  • io包中最重要的是兩個(gè)接口:Reader和Writer Reader接口##### type Writer int...
    勿以浮沙筑高臺(tái)閱讀 16,464評(píng)論 0 5
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評(píng)論 19 139
  • 周佳琦
    周伙英閱讀 307評(píng)論 0 0
  • 高考的大軍已然過(guò)境,我們這群考生站在兩道緊閉的中間不知所措,我們面前是大學(xué)的門(mén),我們身后是高考的門(mén)。在這個(gè)尷尬的地...
    QueenieZz閱讀 424評(píng)論 3 3

友情鏈接更多精彩內(nèi)容