| 会员登录 立即注册

打印 上一主题 下一主题

在 Go 中复制文件的三种方法

[复制链接]
跳转到指定楼层
楼主
3AAA 发表于 2022-6-3 09:59:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
楼主
3AAA
2022-6-3 09:59:31 3108 0 看楼主


本文是 Go 系列的第三篇文章,我将介绍三种最流行的复制文件的方法。

本文将介绍展示如何使用 Go 编程语言来复制文件。在 Go 中复制文件的方法有很多,我只介绍三种最常见的:使用 Go 库中的io.Copy函数调用、一次读取输入文件并将其写入另一个文件,以及使用缓冲区一块块地复制文件。

方法一:使用 io.Copy


第一种方法就是使用 Go 标准库的 io.Copy函数。你可以在copy函数的代码中找到它的实现逻辑,如下所示:
func copy(src, dst string) (int64, error) {  sourceFileStat, err := os.Stat(src)  if err != nil {    return 0, err  }  if !sourceFileStat.Mode.IsRegular {    return 0, fmt.Errorf("%s is not a regular file", src)  }  source, err := os.Open(src)  if err != nil {    return 0, err  }  defer source.Close  destination, err := os.Create(dst)  if err != nil {    return 0, err  }  defer destination.Close  nBytes, err := io.Copy(destination, source)    return nBytes, err  }
首先,上述代码做了两个判断,以便确定它可以被打开读取:一是判断将要复制的文件是否存在(os.Stat(src)),二是判断它是否为常规文件(sourceFileStat.Mode().IsRegular())。剩下的所有工作都由io.Copy(destination, source)这行代码来完成。io.Copy函数执行结束后,会返回复制的字节数和复制过程中发生的第一条错误消息。在 Go 中,如果没有错误消息,错误变量的值就为nil。

你可以在 io 包的文档页面了解有关io.Copy函数的更多信息。

运行 cp1.go将产生以下输出:
$ go run cp1.goPlease provide two command line arguments!$ go run cp1.go fileCP.txt /tmp/fileCPCOPYCopied 3826 bytes!$ diff fileCP.txt /tmp/fileCPCOPY
这个方法已经非常简单了,不过它没有为开发者提供灵活性。这并不总是一件坏事,但是,有些时候,开发者可能会需要/想要告诉程序该如何读取文件。

方法二:使用 ioutil.WriteFile 和 ioutil.ReadFile


复制文件的第二种方法是使用 ioutil.ReadFile和ioutil.WriteFile函数。第一个函数用于将整个文件的内容,一次性地读入到某个内存中的字节切片里;第二个函数则用于将字节切片的内容写入到一个磁盘文件中。

实现代码如下:
input, err := ioutil.ReadFile(sourceFile)if err != nil {  fmt.Println(err)  return}err = ioutil.WriteFile(destinationFile, input, 0644)if err != nil {  fmt.Println("Error creating", destinationFile)  fmt.Println(err)  return}
上述代码包括了两个 if代码块(嗯,用 Go 写程序就是这样的),程序的实际功能其实体现在ioutil.ReadFile和ioutil.WriteFile这两行代码中。

运行 cp2.go,你会得到下面的输出:
$ go run cp2.goPlease provide two command line arguments!$ go run cp2.go fileCP.txt /tmp/copyFileCP$ diff fileCP.txt /tmp/copyFileCP
请注意,虽然这种方法能够实现文件复制,但它在复制大文件时的效率可能不高。这是因为当文件很大时,ioutil.ReadFile返回的字节切片会很大。

方法三:使用 os.Read 和 os.Write


在 Go 中复制文件的第三种方法就是下面要介绍的 cp3.go。它接受三个参数:输入文件名、输出文件名和缓冲区大小。

cp3.go最重要的部分位于以下for循环中,你可以在copy函数中找到它,如下所示:
buf := make([]byte, BUFFERSIZE)for {  n, err := source.Read(buf)  if err != nil && err != io.EOF {    return err  }  if n == 0 {    break  }  if _, err := destination.Write(buf[:n]); err != nil {    return err  }}
该方法使用 os.Read将输入文件的一小部分读入名为buf的缓冲区,然后使用os.Write将该缓冲区的内容写入文件。当读取出错或到达文件末尾(io.EOF)时,复制过程将停止。

运行 cp3.go,你会得到下面的输出:
$ go run cp3.gousage: cp3 source destination BUFFERSIZE$ go run cp3.go fileCP.txt /tmp/buf10 10Copying fileCP.txt to /tmp/buf10$ go run cp3.go fileCP.txt /tmp/buf20 20Copying fileCP.txt to /tmp/buf20
在接下来的基准测试中,你会发现,缓冲区的大小极大地影响了 cp3.go的性能。

运行基准测试


在本文的最后一部分,我将尝试比较这三个程序以及 cp3.go在不同缓冲区大小下的性能(使用time(1)命令行工具)。

以下输出显示了复制 500MB 大小的文件时,cp1.go、cp2.go和cp3.go的性能对比:
$ ls -l INPUT-rw-r--r--  1 mtsouk  staff  512000000 Jun  5 09:39 INPUT$ time go run cp1.go INPUT /tmp/cp1Copied 512000000 bytes!real    0m0.980suser    0m0.219ssys     0m0.719s$ time go run cp2.go INPUT /tmp/cp2real    0m1.139suser    0m0.196ssys     0m0.654s$ time go run cp3.go INPUT /tmp/cp3 1000000Copying INPUT to /tmp/cp3real    0m1.025suser    0m0.195ssys     0m0.486s
我们可以看出,这三个程序的性能非常接近,这意味着 Go 标准库函数的实现非常聪明、经过了充分优化。

现在,让我们测试一下缓冲区大小对 cp3.go的性能有什么影响吧!执行cp3.go,并分别指定缓冲区大小为 10、20 和 1000 字节,在一台运行很快的机器上复制 500MB 文件,得到的结果如下:
$ ls -l INPUT-rw-r--r--  1 mtsouk  staff  512000000 Jun  5 09:39 INPUT$ time go run cp3.go INPUT /tmp/buf10 10Copying INPUT to /tmp/buf10real    6m39.721suser    1m18.457ssys 5m19.186s$ time go run cp3.go INPUT /tmp/buf20 20Copying INPUT to /tmp/buf20real    3m20.819suser    0m39.444ssys 2m40.380s$ time go run cp3.go INPUT /tmp/buf1000 1000Copying INPUT to /tmp/buf1000real    0m4.916suser    0m1.001ssys     0m3.986s
我们可以发现,缓冲区越大,cp3.go运行得就越快,这或多或少是符合预期的。此外,使用小于 20 字节的缓冲区来复制大文件会非常缓慢,应该避免。

你可以在 GitHub找到cp1.go、cp2.go和cp3.go的 Go 代码。

如果你有任何问题或反馈,请在(原文)下方发表评论或在 Twitter上与我(原作者)联系。

via: https://opensource.com/article/18/6/copying-files-go

作者:Mihalis Tsoukalos选题:lkxed译者:lkxed校对:wxy

本文由 LCTT原创编译,Linux中国荣誉推出
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则 返回列表

3AAA 当前离线
白银会员

查看:3108 | 回复:0

关于我们  |   侵权投诉受理  |   联系我们  |   Archiver  |  
免责声明:邳州信息网所有言论只代表发表者个人观点,与本站无关
Copyright © 2009-2025 pzxxw.com 版权所有:邳州金银杏文化传媒有限公司  

苏公网安备 32038202000401号

快速回复 返回顶部 返回列表