golang怎么通过io包进行文件读写

其他教程   发布日期:2025年04月20日   浏览次数:160

本文小编为大家详细介绍“golang怎么通过io包进行文件读写”,内容详细,步骤清晰,细节处理妥当,希望这篇“golang怎么通过io包进行文件读写”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

  1. golang
语言中,实现io的读与写,只要实现了如下的读写接口即可:
  1. // go 1.19/src/io/io.go
  2. type Reader interface {
  3. Read(p []byte) (n int, err error)
  4. }
  5. type Writer interface {
  6. Write(p []byte) (n int, err error)
  7. }

  1. golang
语言自身提供了很多实现这两个接口的结构体,比较典型的有:
  1. ioosbytes.bufferstringsbufio
等,这些解决了文件的读写,字节、字符串的读写,或者是带缓冲的读写等。

今天我们先来看看

  1. io
中提供的这些接口,简单了解下相关原理。

1.io包的读写函数

典型的相关函数声明如下:

  1. // 读操作
  2. func ReadAll(r Reader) ([]byte, error) {...}
  3. // 写操作
  4. func Copy(dst Writer, src Reader) (written int64, err error) {...}
  5. func WriteString(w Writer, s string) (n int, err error) {...}

在上面的

  1. ReadAll()
函数中,接收一个
  1. Reader
类型的参数,比如
  1. os.File
类型,又或者是其他的实现了
  1. io.Reader
接口的结构体类型,输出读取到的内容,以字节数组形式输出,外附一个
  1. error
错误,我们进一步看看其内部实现:
  1. func ReadAll(r Reader) ([]byte, error) {
  2. // 新建字节数组,cap=512
  3. b := make([]byte, 0, 512)
  4. // 开启循环读取内容
  5. for {
  6. //
  7. if len(b) == cap(b) {
  8. // Add more capacity (let append pick how much).
  9. b = append(b, 0)[:len(b)]
  10. }
  11. // 读取 reader中的内容,填充到对应部分
  12. n, err := r.Read(b[len(b):cap(b)])
  13. b = b[:len(b)+n]
  14. // 如果读到了结尾,可以返回数据
  15. if err != nil {
  16. if err == EOF {
  17. err = nil
  18. }
  19. return b, err
  20. }
  21. }
  22. }

从上面源码可以知道,

  1. io.ReadAll()
函数,通过一次读取
  1. Reader
中的所有内容,如果是小文件无所谓,占用内存有限,但如果是好几个G的文件呢,是不是,本来服务器内存有限,以下占用几G内存,这样读取不太合理,当然,
  1. golang
也为我们提供了带缓冲的读取,这是后话,后面再讨论。

接下来看看写入操作的源码实现:

  1. // io.Copy()
  2. func Copy(dst Writer, src Reader) (written int64, err error) {
  3. return copyBuffer(dst, src, nil) // 实际通过调用此函数实现
  4. }
  5. func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
  6. if buf != nil && len(buf) == 0 { // 当buf非空或者长度0,panic
  7. panic("empty buffer in CopyBuffer")
  8. }
  9. return copyBuffer(dst, src, buf) // 继续往下调用
  10. }
  11. func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
  12. // If the reader has a WriteTo method, use it to do the copy.
  13. // Avoids an allocation and a copy.
  14. if wt, ok := src.(WriterTo); ok { // 源实现了writeTo就可以直接调用结束
  15. return wt.WriteTo(dst)
  16. }
  17. // Similarly, if the writer has a ReadFrom method, use it to do the copy.
  18. if rt, ok := dst.(ReaderFrom); ok { // 目标实现了readFrom,也可以直接调用结束
  19. return rt.ReadFrom(src)
  20. }
  21. if buf == nil {
  22. size := 32 * 1024
  23. if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
  24. if l.N < 1 {
  25. size = 1
  26. } else {
  27. size = int(l.N)
  28. }
  29. }
  30. buf = make([]byte, size) // buf空时,创建buf
  31. }
  32. // 开启循环内容读取
  33. for {
  34. nr, er := src.Read(buf) // 从源中读取内容到buf中
  35. if nr > 0 {
  36. nw, ew := dst.Write(buf[0:nr]) // 目标w从buf中写入内容
  37. if nw < 0 || nr < nw {
  38. nw = 0
  39. if ew == nil {
  40. ew = errInvalidWrite
  41. }
  42. }
  43. written += int64(nw) // 更新写入长度
  44. if ew != nil { // 写入返回err,退出循环
  45. err = ew
  46. break
  47. }
  48. if nr != nw { // 读取与写入长度不等,退出
  49. err = ErrShortWrite
  50. break
  51. }
  52. }
  53. if er != nil { // 读err非空,退出
  54. if er != EOF {
  55. err = er
  56. }
  57. break
  58. }
  59. }
  60. return written, err // 返回写入长度及err
  61. }

通过源码走读,我们可以看到,在函数中传入

  1. Writer
作为目标写入对象,传入
  1. Reader
作为源读取对象,如果
  1. Reader
对象实现
  1. WriteTo
方法,我们可以直接调用此完成
  1. copy
, 或者是
  1. Writer
对象实现了
  1. ReadFrom
方法,我们可以直接调用此完成
  1. copy
,都没有实现,就只有通过
  1. buf
作为中转,通过循环,先从源读取内容,再写入目标对象中,最后返回整个内容的长度,当然其中也有相关错误处理,这里不做讨论。

接下来

  1. io
中提供的另外的写入操作实现:
  1. // io.WriteString()
  2. func WriteString(w Writer, s string) (n int, err error) {
  3. if sw, ok := w.(StringWriter); ok { // 如w实现了 StringWriter,则调用相关方法实现写入
  4. return sw.WriteString(s)
  5. }
  6. return w.Write([]byte(s)) // 作为兜底,w对象是肯定实现了 Write() 方法,所以调用此方法实现写入
  7. }

从上面代码可以看到,该实现逻辑简单,具体见注释。

  1. io包实现读写操作
准备:
  • file.txt,作为读取源,然后再写入其他文件中 写操作,我们实现了2种,一种

    1. io.Copy()
    ,一种
    1. io.WriteString()
    ,具体使用见下面代码:
  1. func ioRW() {
  2. // read
  3. f, err := os.Open("D:demo1srcdemo23go-iofilefile.txt")
  4. defer f.Close()
  5. if err != nil {
  6. fmt.Printf("err: %s
  7. ", err)
  8. return
  9. }
  10. b, err := io.ReadAll(f)
  11. if err != nil {
  12. fmt.Printf("err: %s
  13. ", err)
  14. return
  15. }
  16. fmt.Printf("ioRW read content:
  17. %s
  18. ", b)
  19. // write
  20. // 1.io.Copy()
  21. fw, err := os.Create("io_w.txt")
  22. defer fw.Close()
  23. n, err := io.Copy(fw, strings.NewReader(string(b)))
  24. if err != nil {
  25. fmt.Printf("err: %s
  26. ", err)
  27. return
  28. }
  29. // 2.io.WriteString()
  30. fw_1, _ := os.Create("io_w_1.txt")
  31. defer fw_1.Close()
  32. n, err := io.WriteString(fw_1, string(b))
  33. if err != nil {
  34. fmt.Printf("err: %s
  35. ", err)
  36. return
  37. }
  38. fmt.Printf("ioRW write size: %d
  39. ", n)
  40. }

2.io.ioutil包实现读写操作

所谓

  1. util
,肯定是作为工具使用,怎么方便怎么来,我们甚至通过文件名就可以完成操作,具体实现细节我们不关心,总之它实现了这些功能。

  1. 注意
由于我使用的
  1. go 1.19
,在源码中,明确声明在
  1. go 1.16
后,逐渐弃用了,所以使用高版本的
  1. go
时要注意这个问题,这里为了增加源码的了解熟悉,也拿出来作为分享。

  1. 读操作
  1. // Deprecated: As of Go 1.16, this function simply calls io.ReadAll.
  2. func ReadAll(r io.Reader) ([]byte, error) {
  3. return io.ReadAll(r)
  4. }
  5. // Deprecated: As of Go 1.16, this function simply calls os.ReadFile.
  6. func ReadFile(filename string) ([]byte, error) {
  7. return os.ReadFile(filename)
  8. }

  1. ReadAll
中,实际也是调用上面的
  1. io.ReadAll()
,这里不赘述,在
  1. ReadFile
中,我们传入个文件名,就可以读取到整个字节内容,实际它也是调用
  1. os.ReadFile()
实现,后面再讨论
  1. os

  1. 写操作
  1. // Deprecated: As of Go 1.16, this function simply calls os.WriteFile.
  2. func WriteFile(filename string, data []byte, perm fs.FileMode) error {
  3. return os.WriteFile(filename, data, perm)
  4. }

可以看到,这里也是调用了

  1. os.WriteFile()
实现文件的写入,只需要我们传入待写入的文件名,写入的字节数组,以及写入文件的权限,是不是很简单。

  1. 代码实用
  1. func ioutilRW() {
  2. // read
  3. b, err := ioutil.ReadFile("D:demo1srcdemo23go-iofilefile.txt")
  4. if err != nil {
  5. fmt.Printf("err: %s
  6. ", err)
  7. return
  8. }
  9. fmt.Printf("ioutilRW read content:
  10. %s
  11. ", b)
  12. // write
  13. err = ioutil.WriteFile("ioutilRW_w.txt", b, 664)
  14. if err != nil {
  15. fmt.Printf("err: %s
  16. ", err)
  17. return
  18. }
  19. fmt.Println("ioutilRW write err: ", err)
  20. }

以上就是golang怎么通过io包进行文件读写的详细内容,更多关于golang怎么通过io包进行文件读写的资料请关注九品源码其它相关文章!