一道Go练习题引发的思考

我像麋鹿一样在林荫中走着,为着自己的香气而发狂;夜晚是五月正中的夜晚,清风是南国的清风; 我迷路了,我游荡着,我寻求那得不到的东西,我得到了我所没有寻求的东西。 ------ 泰戈尔

前沿

当我在看 《Go程序设计语言》这本书第13页时,练习题1.7提到,用Copy函数替换ReadAll,还接了一句这样就不需要装下整个数据流的缓冲区,这让我不得不思考,这两的差异是啥,当时想着难道一个是全部一次性读完,一个是一行一行或者一点一点的读?,既然有疑问,那还是搞懂吧。

在搜索一番后,也没有搜索到啥有用的东西,或者可能是我的方式不对,这就很烦躁了,然后又想着学Go不就是看源码的意思嘛,随机硬着头皮打开源码,开始梳理,硬着头皮写下这篇文章,免得明天就忘记啦!

ReadAll

Go 复制代码
func ReadAll(r Reader) ([]byte, error) {
    b := make([]byte, 0, 512)
    for {
       n, err := r.Read(b[len(b):cap(b)])
       b = b[:len(b)+n]
       if err != nil {
          if err == EOF {
             err = nil
          }
          return b, err
       }

       if len(b) == cap(b) {
          // Add more capacity (let append pick how much).
          b = append(b, 0)[:len(b)]
       }
    }
}

首先创建一个 []byte类型,长度为0,容量为512的切片b,再直接进入一个无限循环

r.Read(b[len(b):cap(b)),第一眼看不懂,那就查吧,查看文档,解释是Read reads data into p. It returns the number of bytes read into p.也就是将r读入b[len(b):cap(b),返回读入的字节数

一直扩展b的长度用来包含读入的数据,接着我们看到了这个函数的 return 语句,这个return的条件是,报错,或者是读完了才会return这个切片b

还可以看到,如果没有读完,但是b容量不够,还会进行扩容,直到读取完成

所以可以很清晰的知道了ReadAll就是将一个Reader中的所有内容读取完毕后一次性返回

Copy

Go 复制代码
func Copy(dst Writer, src Reader) (written int64, err error) {
    return copyBuffer(dst, src, nil)
}

func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
    if buf != nil && len(buf) == 0 {
       panic("empty buffer in CopyBuffer")
    }
    return copyBuffer(dst, src, buf)
}

func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
    // 删减代码......
 
    for {
       nr, er := src.Read(buf)
       if nr > 0 {
          nw, ew := dst.Write(buf[0:nr])
          if nw < 0 || nr < nw {
             nw = 0
             if ew == nil {
                ew = errInvalidWrite
             }
          }
       }
       if er != nil {
          if er != EOF {
             err = er
          }
          break
       }
    }
    return written, err
}

起初一行一行看,没看懂,后来删了点代码,然后加上点猜,好像能理解一点了,同样有个无限循环,也有缓存区,不同的是,每次读入缓存区buf后,都会直接将缓存区的内容写入目标写入器dst.Write(buf[0:nr),nr同样是读入的多少就是多少(缓冲区大小是:32*1024)

结束同样是读完或者报错(其实还有其它的结束条件,删除了)

总结

  • ReadAll 和 Copy 都使用了新的内存区域来存储从目标文件中读取的数据,ReadAll是存储了读到的全部数据,相当于内存直接复制了一遍,Copy是一个缓冲区,每次会读一部分,就会将读取到的字写入到目标写入器中,但是这个缓冲区大小32*1024
  • EOF用来表示对一个文件的读取成功,也就是读取到了目标文件的最后一行
  • 当数据量大的时候千万别用 ReadAll,数据量小,影响不大
相关推荐
Mcband1 分钟前
Spring Boot 整合 ShedLock 处理定时任务重复执行的问题
java·spring boot·后端
无名-CODING12 分钟前
Spring Bean生命周期详解:从入门到精通
java·后端·spring
源代码•宸13 分钟前
大厂技术岗面试之一面(准备自我介绍、反问)
经验分享·后端·算法·面试·职场和发展·golang·反问
晚风吹长发11 小时前
初步了解Linux中的动静态库及其制作和使用
linux·运维·服务器·数据结构·c++·后端·算法
梁下轻语的秋缘12 小时前
ESP32-WROOM-32E存储全解析:RAM/Flash/SD卡读写与速度对比
java·后端·spring
wanzhong233312 小时前
开发日记8-优化接口使其更规范
java·后端·springboot
羊小猪~~14 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
宇宙帅猴14 小时前
【Ubuntu踩坑及解决方案(一)】
linux·运维·ubuntu·go
张彦峰ZYF14 小时前
商品供给域的工程化简要设计考量
后端·系统架构·商品模型·商品供给
小北方城市网15 小时前
微服务注册中心与配置中心实战(Nacos 版):实现服务治理与配置统一
人工智能·后端·安全·职场和发展·wpf·restful