![aa1ccfb2c9e6d6d4b0e1efd784168374.png](https://i-blog.csdnimg.cn/blog_migrate/40fefb8f50bb2d1825eb784543c71e74.jpeg)
本文将向你展示如何使用Go编程语言复制文件。尽管在Go中复制文件有三种以上的方法,但本文将介绍三种最常见的方法:使用io.Copy()Go库中的函数调用;一次读取所有输入文件并将其写入另一个文件;并使用缓冲区将文件分小块复制。
方法1:使用io.Copy()
该实用程序的第一个版本将使用io.Copy()标准Go库的功能。该实用程序的逻辑可以在该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.Copy()在io软件包文档页面上了解有关该功能的更多信息。
执行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
该技术尽可能简单,但是给开发人员没有灵活性,这并不总是一件坏事。但是,有时开发人员需要或想要决定他们如何读取文件。
方法2:使用ioutil.WriteFile()和ioutil.ReadFile()
复制文件的第二种方法是使用ioutil.ReadFile()和ioutil.WriteFile()函数。第一个函数将整个文件的内容读取到一个字节片中,第二个函数将一个字节片的内容写入一个文件中。
该实用程序的逻辑可以在以下Go代码中找到:
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()and 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()也将非常大。
方法3:使用os.Read()和os.Write()
在Go中复制文件的第三种方法是使用cp3.go 将在本节中开发的实用程序。它接受三个参数:输入文件的文件名,输出文件的文件名和缓冲区的大小。
的最重要部分cp3.go位于以下for循环中,可以在copy() function:
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将生成下一种输出:
![fbd785b441b7092c445c35957dc27c69.png](https://i-blog.csdnimg.cn/blog_migrate/e5d291933155920a3dc296aed74a75eb.jpeg)
如你所见,缓冲区的大小会极大地影响的性能cp3.go。
做一些基准测试
本文的最后一部分将尝试cp3.go 使用time(1)命令行实用程序比较这三个程序以及各种缓冲区大小的性能。
下面的输出显示的性能cp1.go,cp2.go以及cp3.go复制一个500MB的文件时:
![51185052193eb8df24dfa9ae5e08a715.png](https://i-blog.csdnimg.cn/blog_migrate/9ad97dbfafab2197cfef8b88dfa8e916.jpeg)
输出显示所有三个实用程序的性能都非常相似,这意味着标准Go库的功能非常聪明且经过优化。
现在,让我们测试缓冲区大小如何影响的性能cp3.go。cp3.go以10、20和1,000字节的缓冲区大小执行以在相当快的计算机上复制500MB文件将产生以下结果:
![deacbfff41e9e2b68591a44f6a9ace46.png](https://i-blog.csdnimg.cn/blog_migrate/2abfef91d0c6f965c07cd79b081f9d99.jpeg)
生成的输出表明,缓冲区越大,cp3.go实用程序的性能越快,这或多或少是期望的。此外,使用小于20字节的缓冲区大小复制大文件是一个非常缓慢的过程,应避免使用。