作为一个golang新手,对go的读写相关的操作一直都没有理解透彻,尤其是很多的方法都需要传入一个read或者write的操作,对于这种一直都不理解,而且对于读写也是不太会使用。所以希望通过自己整理标准库io相关的的数据来帮助自己理解这方面的知识。
下面开始正文
首先,io操作,或者io流,其实也就是读写操作和读写流,就是对数据的读或者写,都可以称之为io操作。
在官方解释中我们可以看到,使用者不能默认io包的操作时并发安全的,因为go在实际开发过程中会有很多的并发情况,而且go也是因高并发著名,所以在任何的操作我们都应当考虑是否需要并发安全性。
在开始讲解包内容之前我们需要知道一个错误,也就是EOF,end of file 的缩写意思是文件结尾。这个错误是用来提醒使用者读取到了文件的结尾,并不是出现了错误,会经常用到。
首先来看reader
type Reader
type Reader interface{ |
我们可以看到这是一个interface,也就是一个接口类型,是没办法直接使用,这个接口里面实现了read方法,当你定义了一个结构体,而这个结构体实现了一个read方法,那么你写的结构体是不是也实现了Reader这个接口呢?
所以这个接口是给外部实现使用的,比如我们了解的os操作,我们打开一个文件之后,是不是返回的有一个os.File类型的数据,而这个数据就有read方法去读数据,原因就是它实现了基本的Reader接口。下面用代码来接收一下吧
import (
"IMproject/webgin"
"fmt"
"io"
"log"
"os"
)
func main() {
f, err := os.Open("a.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close() //文件使用完成后一定要记得关闭文件,在实际的开发中会经常有文件相关的操作,如果不及时的关闭文件会造成资源的浪费,严重了会发生事故。
buf:=make([]byte,100)//实例化出一个缓冲区,因为read方法需要传入一个这样的参数
//不知道文件的大小所以要循环从文件中读取数据
for{
n,err:=f.Read(buf)
if err != nil&&err!=io.EOF {
fmt.Printf("read err:%v",err)
}
if n==0||err==io.EOF{
fmt.Println("文件读取完毕")
break
}
fmt.Println(string(buf[:n]))
}
}
type Writer
type Writer interface{ } |
同样的道理,Writer接口也是同样给外部实现提供的,所以如果有结构体存在这个方法,也就实现了这个接口。这里就不过多的解释了。
关于每个方法的具体使用请移步到官方的中文文档,百度直接搜就行。
相信到这里应该都能够理解官方文档中的所有接口了,这些并不是给外部调用的,所以,我们也没有必要去调用这些内容,需要的时候去实现就行了。我这里偷个懒就不去每个都去做解释了。
下面解释结构体
结构体大家都很熟悉,我们定义结构体时,很多情况下都是为了给他加上一些方法。标准库中亦是如此。
举例说明
type LimiteReader stuct{ N int64 } |
该结构体实现了一个Read方法
func (l *LimitedReader) Read(p []byte) (n int, err error) |
作用是限制了读取的大小是N个字节,实际上是对P取0到N的切片,然后将结果传给R。如果P的大小没有这么大,就会从新设置N,也就是剩余可以使用的字节大小。这个可以去源码中看,还是比较简单的。
另外获取LimiteReader结构体可以使用如下函数
func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} } |
它会返回一个LimiteReader结构体。
供外部调用的函数
我们多数也是使用io包提供的一下函数,但是理解上面的都是也是很很重要的。理解了上面的内容之后,你就能够更好的理解,io包提供的函数是怎么回事了。写到这里我就大致的明白其他的一些函数中的reader、writer参数了。也就是实现了Reader、Writer接口的结构体罢了。
搞明白这些之后剩下的函数就没有必要每个都去写一下了。我写这个博客的目的也是帮助自己梳理逻辑,所以并不会有太多实际的内容。能不能帮助到读者就看我们是不是有相同的问题了。
推荐一个博客,具体io标准库可以去这里看:https://blog.csdn.net/qq_39280718/article/details/125704311
写的非常的好。
io/ioutil
这个包对io包中的方法做了进一步的封装,封装了一下os操作,比如从文件中读写的操作,就是封装了打开和关闭文件,这些都是os操作,这个包中的方法也是经常使用的。这里不做讲解,主要是没啥可讲的,记住怎么用就行了,上面讲的理解了,这些就只是一些操作。
bufio
该包的使用,首先你需要先定义出一个带有缓冲区的Reader或者Writer,你可以使用它提供的相关new方法获得相应的结构体,然后再使用结构体对应的方法。
func NewReader(rd io.Reader) *Reader func NewReaderSize(rd io.Reader, size int) *Reader func NewWriter(w io.Writer) *Writer func NewWriterSize(w io.Writer, size int) *Writer func NewReadWriter(r *Reader, w *Writer) *ReadWriter |
分别是:得到默认缓冲区大小的reader
得到指定缓冲区大小的reader
得到默认缓冲区大小的writer
得到指定缓冲区大小的writer
得到默认缓冲区大小的readwriter
这几种方法都需要传入io的reader或者writer,到这又该纳闷了?这里的参数哪里来啊?你是不是也是这么想的啊?我是这么想的。
其实,bufio中的结构体是包装了io中的read,writer类,我们上面说了只要结构体实现了io中的Read,Write方法,就实现了对应的接口。其实也就可以认为是一类数据了。也就可以当做相同类型的参数传递了。
举例说明:
func test() {
reader := strings.NewReader("abcdefg") //返回strings.reader结构体,点进源码可以看到实现了Read方法。所以可以认为是io.Reader去传参
buf_reader := bufio.NewReader(reader)
str, _ := buf_reader.ReadString('\n')
fmt.Println(str)
}
到这里大概也能梳理好io,ioutil,bufio之间的关系了。