在io包中主要是操作流的一些方法,今天主要学习一下copy。
在io包(golang 版本 1.12)中,提供了3个公开的copy方法:CopyN(),Copy(),CopyBuffer().
CopyN(dst,src,n) 为复制src 中 n 个字节到 dst。
Copy(dst,src) 为复制src 全部到 dst 中。
CopyBuffer(dst,src,buf)为指定一个buf缓存区,以这个大小完全复制。
他们的关系如下:
从图可以看出,无论是哪个copy方法最终都是由copyBuffer()这个私有方法实现的。下面我们看看这个方法的源码。
func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
\t// If the reader has a WriteTo method, use it to do the copy.
\t// Avoids an allocation and a copy.
\tif wt, ok := src.(WriterTo); ok {
\t\treturn wt.WriteTo(dst)
\t}
\t// Similarly, if the writer has a ReadFrom method, use it to do the copy.
\tif rt, ok := dst.(ReaderFrom); ok {
\t\treturn rt.ReadFrom(src)
\t}
\tif buf == nil {
\t\tsize := 32 * 1024
\t\tif l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
\t\t\tif l.N < 1 {
\t\t\t\tsize = 1
\t\t\t} else {
\t\t\t\tsize = int(l.N)
\t\t\t}
\t\t}
\t\tbuf = make([]byte, size)
\t}
\tfor {
\t\tnr, er := src.Read(buf)
\t\tif nr > 0 {
\t\t\tnw, ew := dst.Write(buf[0:nr])
\t\t\tif nw > 0 {
\t\t\t\twritten += int64(nw)
\t\t\t}
\t\t\tif ew != nil {
\t\t\t\terr = ew
\t\t\t\tbreak
\t\t\t}
\t\t\tif nr != nw {
\t\t\t\terr = ErrShortWrite
\t\t\t\tbreak
\t\t\t}
\t\t}
\t\tif er != nil {
\t\t\tif er != EOF {
\t\t\t\terr = er
\t\t\t}
\t\t\tbreak
\t\t}
\t}
\treturn written, err
}
从这部分代码可以看出,复制主要分为3种。
1.如果被复制的Reader(src)会尝试能否断言成writerTo,如果可以则直接调用下面的writerTo方法
2.如果 Writer(dst) 会尝试能否断言成ReadFrom ,如果可以则直接调用下面的readfrom方法
3.如果都木有实现,则调用底层read实现复制。
其中,有这么一段代码:
if buf == nil {
\t\tsize := 32 * 1024
\t\tif l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
\t\t\tif l.N < 1 {
\t\t\t\tsize = 1
\t\t\t} else {
\t\t\t\tsize = int(l.N)
\t\t\t}
\t\t}
\t\tbuf = make([]byte, size)
\t}
这部分主要是实现了对Copy和CopyN的处理。通过上面的调用关系图,我们看出CopyN在调用后,会把Reader转成LimiteReader。
区别是如果Copy,直接建立一个缓存区默认大小为 32* 1024 的buf,如果是CopyN 会先判断 要复制的字节数 如果小于默认大小,会创建一个等于要复制字节数的buf。
閱讀更多 雨竹165179353 的文章