Redian新闻
>
在 Go 中复制文件的三种方法 | Linux 中国

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

科技
 
导读:本文是 Go 系列的第三篇文章,我将介绍三种最流行的复制文件的方法。                     
本文字数:4840,阅读时长大约:5分钟

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

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

方法一:使用 io.Copy()

第一种方法就是使用 Go 标准库的 io.Copy() 函数。你可以在 copy() 函数的代码中找到它的实现逻辑,如下所示:

  1. func copy(src, dst string) (int64, error) {
  2. sourceFileStat, err := os.Stat(src)
  3. if err != nil {
  4. return 0, err
  5. }
  6. if !sourceFileStat.Mode().IsRegular() {
  7. return 0, fmt.Errorf("%s is not a regular file", src)
  8. }
  9. source, err := os.Open(src)
  10. if err != nil {
  11. return 0, err
  12. }
  13. defer source.Close()
  14. destination, err := os.Create(dst)
  15. if err != nil {
  16. return 0, err
  17. }
  18. defer destination.Close()
  19. nBytes, err := io.Copy(destination, source)
  20. return nBytes, err
  21. }

首先,上述代码做了两个判断,以便确定它可以被打开读取:一是判断将要复制的文件是否存在(os.Stat(src)),二是判断它是否为常规文件(sourceFileStat.Mode().IsRegular())。剩下的所有工作都由 io.Copy(destination, source) 这行代码来完成。io.Copy() 函数执行结束后,会返回复制的字节数和复制过程中发生的第一条错误消息。在 Go 中,如果没有错误消息,错误变量的值就为 nil

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

运行 cp1.go 将产生以下输出:

  1. $ go run cp1.go
  2. Please provide two command line arguments!
  3. $ go run cp1.go fileCP.txt /tmp/fileCPCOPY
  4. Copied 3826 bytes!
  5. $ diff fileCP.txt /tmp/fileCPCOPY

这个方法已经非常简单了,不过它没有为开发者提供灵活性。这并不总是一件坏事,但是,有些时候,开发者可能会需要/想要告诉程序该如何读取文件。

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

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

实现代码如下:

  1. input, err := ioutil.ReadFile(sourceFile)
  2. if err != nil {
  3. fmt.Println(err)
  4. return
  5. }
  6. err = ioutil.WriteFile(destinationFile, input, 0644)
  7. if err != nil {
  8. fmt.Println("Error creating", destinationFile)
  9. fmt.Println(err)
  10. return
  11. }

上述代码包括了两个 if 代码块(嗯,用 Go 写程序就是这样的),程序的实际功能其实体现在 ioutil.ReadFile() 和 ioutil.WriteFile() 这两行代码中。

运行 cp2.go,你会得到下面的输出:

  1. $ go run cp2.go
  2. Please provide two command line arguments!
  3. $ go run cp2.go fileCP.txt /tmp/copyFileCP
  4. $ diff fileCP.txt /tmp/copyFileCP

请注意,虽然这种方法能够实现文件复制,但它在复制大文件时的效率可能不高。这是因为当文件很大时,ioutil.ReadFile() 返回的字节切片会很大。

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

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

cp3.go 最重要的部分位于以下 for 循环中,你可以在 copy() 函数中找到它,如下所示:

  1. buf := make([]byte, BUFFERSIZE)
  2. for {
  3. n, err := source.Read(buf)
  4. if err != nil && err != io.EOF {
  5. return err
  6. }
  7. if n == 0 {
  8. break
  9. }
  10. if _, err := destination.Write(buf[:n]); err != nil {
  11. return err
  12. }
  13. }

该方法使用 os.Read() 将输入文件的一小部分读入名为 buf 的缓冲区,然后使用 os.Write() 将该缓冲区的内容写入文件。当读取出错或到达文件末尾(io.EOF)时,复制过程将停止。

运行 cp3.go,你会得到下面的输出:

  1. $ go run cp3.go
  2. usage: cp3 source destination BUFFERSIZE
  3. $ go run cp3.go fileCP.txt /tmp/buf10 10
  4. Copying fileCP.txt to /tmp/buf10
  5. $ go run cp3.go fileCP.txt /tmp/buf20 20
  6. Copying fileCP.txt to /tmp/buf20

在接下来的基准测试中,你会发现,缓冲区的大小极大地影响了 cp3.go 的性能。

运行基准测试

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

以下输出显示了复制 500MB 大小的文件时,cp1.gocp2.go 和 cp3.go 的性能对比:

  1. $ ls -l INPUT
  2. -rw-r--r--  1 mtsouk  staff  512000000 Jun  5 09:39 INPUT
  3. $ time go run cp1.go INPUT /tmp/cp1
  4. Copied 512000000 bytes!
  5. real    0m0.980s
  6. user    0m0.219s
  7. sys     0m0.719s
  8. $ time go run cp2.go INPUT /tmp/cp2
  9. real    0m1.139s
  10. user    0m0.196s
  11. sys     0m0.654s
  12. $ time go run cp3.go INPUT /tmp/cp3 1000000
  13. Copying INPUT to /tmp/cp3
  14. real    0m1.025s
  15. user    0m0.195s
  16. sys     0m0.486s

我们可以看出,这三个程序的性能非常接近,这意味着 Go 标准库函数的实现非常聪明、经过了充分优化。

现在,让我们测试一下缓冲区大小对 cp3.go 的性能有什么影响吧!执行 cp3.go,并分别指定缓冲区大小为 10、20 和 1000 字节,在一台运行很快的机器上复制 500MB 文件,得到的结果如下:

  1. $ ls -l INPUT
  2. -rw-r--r--  1 mtsouk  staff  512000000 Jun  5 09:39 INPUT
  3. $ time go run cp3.go INPUT /tmp/buf10 10
  4. Copying INPUT to /tmp/buf10
  5. real    6m39.721s
  6. user    1m18.457s
  7. sys 5m19.186s
  8. $ time go run cp3.go INPUT /tmp/buf20 20
  9. Copying INPUT to /tmp/buf20
  10. real    3m20.819s
  11. user    0m39.444s
  12. sys 2m40.380s
  13. $ time go run cp3.go INPUT /tmp/buf1000 1000
  14. Copying INPUT to /tmp/buf1000
  15. real    0m4.916s
  16. user    0m1.001s
  17. sys     0m3.986s

我们可以发现,缓冲区越大,cp3.go 运行得就越快,这或多或少是符合预期的。此外,使用小于 20 字节的缓冲区来复制大文件会非常缓慢,应该避免。

你可以在 GitHub🔗 github.com 找到 cp1.gocp2.go 和 cp3.go 的 Go 代码。

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


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

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

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


LCTT 译者 :六开箱
🌟🌟🌟🌟
翻译: 68.0 篇
|
贡献: 77 天
2022-03-16
2022-05-31
https://linux.cn/lctt/lkxed
欢迎遵照 CC-BY-SA 协议规定转载,
如需转载,请在文章下留言 “转载:公众号名称”,
我们将为您添加白名单,授权“转载文章时可以修改”。


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
在虚拟机中运行 Linux 的十大优点 | Linux 中国论文化的属性如何在 Linux 和 Windows 电脑之间共享文件 | Linux 中国痛经有不同原因,缓解痛经,这三种方法对所有女性都有效!我如何在 Linux 上扫描家庭照片 | Linux 中国用 Gwenview 在 Linux 上裁剪和调整照片大小 | Linux 中国Fedora Linux 36 发布 | Linux 中国在 Linux 上玩电子游戏的三种方式 | Linux 中国分享 8 篇使用 Linux 命令行的技巧 | Linux 中国英伟达在提升 Linux 上的 GPU 使用体验上迈出了一大步 | Linux 中国Puerto Rico 放风用这些开源工具在 Linux 上编辑 PDF 文件 | Linux 中国在 Linux 中隐藏文件和文件夹的那些事 | Linux 中国如何在 Linux 桌面中启用 “激活 Linux” 水印通知 | Linux 中国Linux 中国开通播客频道:“开源朗读者”和“硬核观察” | Linux 中国好消息!Docker Desktop 现已支持 Linux | Linux 中国用 ranger 在 Linux 终端管理你的文件 | Linux 中国你的 Linux 启动时有几只小企鹅? | Linux 中国疫情下的另外较量,对付新冠的三种方法Archinstall 新的菜单系统让安装 Arch Linux 更容易了 | Linux 中国如何在 Linux 中使用 Pandoc 转换文件格式 | Linux 中国实测 Linux Mint 升级工具 | Linux 中国Collision:用于验证 ISO 和其他文件的 Linux 应用 | Linux 中国比侵略更坏的是逼人侵略最适合程序员的 10 款 Linux 发行版 | Linux 中国在 Ubuntu Linux 如何安装 H.264 解码器 | Linux 中国使用 watch 和 tail 命令监视 Linux 上的活动 | Linux 中国如何在 Ubuntu/Linux 和 Windows 之间共享文件夹 | Linux 中国在 Linux 上学习 C 语言的五种方式 | Linux 中国活见鬼了!在 Linux 上使用 Bash 创建一个临时文件 | Linux 中国房租学费政府交,有四娃的退伍军人房客使用 apt 进行 Linux 包管理 | Linux 中国上一个说“丼”不读jǐng的人,已经被我骂哭了使用 dnf 进行 Linux 包管理 | Linux 中国
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。