当前位置: 首页 > 科技观察

如何在Go中将[]byte转换为io.Reader?

时间:2023-03-15 14:36:11 科技观察

在stackoverflow上看到一道题,题目做了一个网络请求,接口返回了[]byte。如果你想把它转换成io.Reader,你需要做什么?这个问题解决起来并不复杂,几行代码就可以轻松转换成功。不仅如此,您还可以通过几行代码将其反向转换回来。听我慢慢吹给你听,先直视这两段代码。[]bytetoio.Readerpackagemainimport("bytes""fmt""log")funcmain(){data:=[]byte("HelloAlwaysBeta")//byteslicetobytes.Reader,实现了io.Reader接口reader:=bytes.NewReader(data)//从readerbuf读取数据:=make([]byte,len(data))if_,err:=reader.Read(buf);err!=nil{log.Fatal(err)}fmt.Println(string(buf))}输出:HelloAlwaysBeta这段代码首先将[]byte数据转换成读卡器,然后从读卡器读取数据,并打印输出。io.Readerturn[]bytepackagemainimport("bytes""fmt""strings")funcmain(){ioReaderData:=strings.NewReader("HelloAlwaysBeta")//createsabytes.Bufferandreadfromio.Readerbuf:=&bytes.Buffer{}buf.ReadFrom(ioReaderData)//retrieveabyteslicefrombytes.Bufferdata:=buf.Bytes()//onlyreadtheleftbytesfrom6fmt.Println(string(data[6:]))}输出:AlwaysBeta这段代码先创建一个reader,然后读取数据到buf,最后打印出去。上面两段代码是[]byte和io.Reader的转换过程。对比这两段代码,不难发现两者都有NewReader。并且在转化过程中,起到了关键作用。那么问题来了,这个NewReader是什么?接下来,我们就通过源码一探究竟。源码解析Go的io包提供了最基本的IO接口,其中io.Reader和io.Writer是最关键的接口,很多原生结构都是围绕这两个接口开发的。下面分别说说这两个接口:Reader接口io.Reader代表一个reader,它从一个资源中读取数据到传输缓冲区。在缓冲区中,数据可以流式传输和使用。接口定义如下:typeReaderinterface{Read(p[]byte)(nint,errerror)}Read()方法将len(p)个字节读入p。它返回读取的字节数n,如果发生错误则返回一条错误消息。举个例子:packagemainimport("fmt""io""os""strings")funcmain(){reader:=strings.NewReader("Clearisbetterthanclever")p:=make([]byte,4)for{n,错误:=reader.Read(p)iferr!=nil{iferr==io.EOF{fmt.Println("EOF:",n)break}fmt.Println(err)os.Exit(1)}fmt.Println(n,string(p[:n]))}}Output:4Clea4ris4bet4ter4than4cle3verEOF:0这段代码连续从阅读器读取数据,每次4个字节,然后打印出来直到结束。返回的最后一个n值可能小于缓冲区大小。Writer接口io.Writer表示从缓冲区读取数据并将数据写入目标资源的编写器。typeWriterinterface{Write(p[]byte)(nint,errerror)}Write方法将len(p)个字节从p写入对象流。它返回从p写入的字节数n,如果发生错误则返回错误消息。举个例子:packagemainimport("bytes""fmt""os")funcmain(){//创建Buffer暂存空间,并写入一个字符串到Buffer中//使用io.Writer的Write方法写入varbufbytes.Bufferbuf.Write([]byte("helloworld,"))//使用Fprintf将字符串拼接成Bufferfmt.Fprintf(&buf,"welcometogolang!")//将Buffer的内容输出到标准输出设备buf.WriteTo(os.Stdout)}Output:helloworld,welcometogolang!bytes.Buffer是一个用来暂存写入数据的结构类型,它实现了io.Writer接口的Write方法。WriteTo方法定义:func(b*Buffer)WriteTo(wio.Writer)(nint64,errerror)WriteTo方法的第一个参数是io.Writer接口类型。转换原理让我们回到文章开头的转换问题。只要实例实现了接口io.Reader中的Read()方法,接口io.Reader就满足了。bytes和strings包都实现了Read()方法。//src/bytes/reader.go//NewReaderreturnssanewReaderreadingfromb.funcNewReader(b[]byte)*Reader{return&Reader{b,0,-1}}//src/strings/reader.go//NewReaderreturnssanewReaderreadingfroms.//类似于bytes。NewBufferStringbutmoreefficientandread-only.funcNewReader(sstring)*Reader{return&Reader{s,0,-1}}调用NewReader时,会返回对应的T.Reader类型,它们都是io.Reader的扩展。这样转换就完成了。小结在开发过程中,难免会进行一些IO操作,包括打印输出、文件读写、网络连接等,Go语言中也提供了一系列标准库来处理这些操作,主要封装在以下包:io:基本IO操作接口。io/ioutil:封装了一些有用的IO函数。fmt:实现IO格式化操作。bufio:实现缓冲IO。net.Conn:网络读写。os.Stdin、os.Stdout:系统标准输入输出。os.File:系统文件操作。bytes:字节相关的IO操作。除了io.Reader和io.Writer,io包中还封装了很多其他的基础接口,比如ReaderAt、WriterAt、ReaderFrom和WriterTo等,这里不再介绍。这部分代码并不复杂,易于阅读,可以加深对接口的理解。我推荐大家看看。