设为首页 加入收藏

TOP

一文了解 io.Copy 函数(一)
2023-07-23 13:25:04 】 浏览:55
Tags:文了解 io.Copy 函数

1. 引言

io.Copy 函数是一个非常好用的函数,能够非常方便得将数据进行拷贝。本文我们将从io.Copy 函数的基本定义出发,讲述其基本使用和实现原理,以及一些注意事项,基于此完成对io.Copy 函数的介绍。

2. 基本说明

2.1 基本定义

Copy函数用于将数据从源(io.Reader)复制到目标(io.Writer)。它会持续复制直到源中的数据全部读取完毕或发生错误,并返回复制的字节数和可能的错误。函数定义如下:

func Copy(dst io.Writer, src io.Reader) (written int64, err error)

其中dst 为目标写入器,用于接收源数据;src则是源读取器,用于提供数据。

2.2 使用示例

下面提供一个使用 io.Copy 实现数据拷贝的代码示例,比便更好得理解和使用Copy函数,代码示例如下:

package main

import (
        "fmt"
        "io"
        "os"
)

func main() {
        fmt.Print("请输入一个字符串:")
        src := readString()
        // 通过io.Copy 函数能够将 src 的全部数据 拷贝到 控制台上输出
        written, err := io.Copy(os.Stdout, src)
        if err != nil {
                fmt.Println("复制过程中发生错误:", err)
                return
        }

        fmt.Printf("\n成功复制了 %d 个字节。\n", written)
}

func readString() io.Reader {
   buffer := make([]byte, 1024)
   n, _ := os.Stdin.Read(buffer)
   // 如果实际读取的字节数少于切片长度,则截取切片
   if n < len(buffer) {
      buffer = buffer[:n]
   }
   return strings.NewReader(string(buffer))
}

在这个例子中,我们首先使用readString函数从标准输入中读取字符串,然后使用strings.NewReader将其包装为io.Reader返回。

然后,我们调用io.Copy函数,将读取到数据全部复制到标准输出(os.Stdout)。最后,我们打印复制的字节数。可以运行这个程序并在终端输入一个字符串,通过Copy函数,程序最终会将字符串打印到终端上。

3. 实现原理

在了解了io.Copy 函数的基本定义和使用后,这里我们来对 io.Copy 函数的实现来进行基本的说明,加深对 io.Copy 函数的理解。

io.Copy基本实现原理如下,首先创建一个缓冲区,用于暂存从源Reader读取到的数据。然后进入一个循环,每次循环从源Reader读取数据,然后存储到之前创建的缓冲区,之后再写入到目标Writer中。不断重复这个过程,直到源Reader返回EOF,此时代表数据已经全部读取完成,io.Copy也完成了从源Reader往目标Writer拷贝全部数据的工作。

在这个过程中,如果往目标Writer写入数据过程中发生错误,亦或者从源Reader读取数据发生错误,此时io.Copy函数将会中断,然后返回对应的错误。下面我们来看io.Copy的实现:

func Copy(dst Writer, src Reader) (written int64, err error) {
   // Copy 函数 调用了 copyBuffer 函数来实现
   return copyBuffer(dst, src, nil)
}

func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
   // 如果 源Reader 实现了 WriterTo 接口,直接调用该方法 将数据写入到 目标Writer 当中
   if wt, ok := src.(WriterTo); ok {
      return wt.WriteTo(dst)
   }
   // 同理,如果 目标Writer 实现了 ReaderFrom 接口,直接调用ReadFrom方法
   if rt, ok := dst.(ReaderFrom); ok {
      return rt.ReadFrom(src)
   }
   // 如果没有传入缓冲区,此时默认 创建一个 缓冲区
   if buf == nil {
      // 默认缓冲区 大小为 32kb
      size := 32 * 1024
      // 如果源Reader 为LimitedReader, 此时比较 可读数据数 和 默认缓冲区,取较小那个
      if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
         if l.N < 1 {
            size = 1
         } else {
            size = int(l.N)
         }
      }
      buf = make([]byte, size)
   }
   for {
      // 调用Read方法 读取数据
      nr, er := src.Read(buf)
      if nr > 0 {
         // 将数据写入到 目标Writer 当中
         nw, ew := dst.Write(buf[0:nr])
         // 判断写入是否 出现了 错误
         if nw < 0 || nr < nw {
            nw = 0
            if ew == nil {
               ew = errInvalidWrite
            }
         }
         // 累加 总写入数据
         written += int64(nw)
         if ew != nil {
            err = ew
            break
         }
         // 写入字节数 小于 读取字节数,此时报错
         if nr != nw {
            err = ErrShortWrite
            break
         }
      }
      if er != nil {
         if er != EOF {
            err = er
         }
         break
      }
   }
   return written, err
}

从上述基本原理和代码实现来看,io.Copy 函数的实现还是非常简单的,就是申请一个缓冲区,然后从源Reader读取一些数据放到缓冲区中,然后再将缓冲区的数据写入到 目标Writer, 如此往复,直到数据全部读取完成。

4. 注意事项

4.1 注意关闭源Reader和目标Writer

在使用io.Copy 进行数据拷贝时,需要指定源Reader 和 目标Writer,当io.Copy 完成数据拷贝工作后,我们需要调用Close 方法关闭 源Reader 和 目标Writer。如果没有适时关闭资源,可能会导致一些不可预料情况的出现。

下面展示一个使用 io.Copy 进行文件复制的代码示例,同时简单说明不适时关闭资源可能导致的问题:

package main

import (
        "fmt"
        "io"
        "os"
)

func main() {
        sourceFile := "source.txt"
        destinationFile := "destination.txt"

        // 打开源文件
        src, err := os.Open(sourceFile)
        if err != nil {
                fmt.Println("无法打开源文件:", err)
                return
        }
        // 调用Close方法
        defer src.Close()

        // 创建目标文件
        dst, err := os.Create(de
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Golang起步篇 下一篇Go优雅的错误处理: 支持错误堆栈,..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目