如何读/写从/到一个文件使用Go

我一直在尝试自己学习Go,但在尝试读取和写入普通文件时,我被难住了。

我可以得到inFile, _ := os.Open(INFILE, 0, 0),但实际上获取文件的内容没有意义,因为read函数以[]byte作为参数。

func (file *File) Read(b []byte) (n int, err Error)
313965 次浏览

看看文档,似乎你应该声明一个类型为[]字节的缓冲区,并将其传递给read,然后读取到这么多字符,并返回实际读取的字符数(和一个错误)。

的文档

Read从文件中读取最多len(b)个字节。它返回读取的字节数和一个Error(如果有的话)。EOF的信号是一个零计数,err设置为EOF。

这样不行吗?

编辑:另外,我认为你可能应该使用bufio包中声明的Reader/Writer接口,而不是使用操作系统包。

试试这个:

package main


import (
"io";
)




func main() {
contents,_ := io.ReadFile("filename");
println(string(contents));
io.WriteFile("filename", contents, 0644);
}

[]byte是字节数组的全部或部分的切片(类似于子字符串)。将slice看作一个值结构,它带有一个隐藏的指针字段,用于系统定位和访问数组(slice)的全部或部分,加上用于slice长度和容量的字段,您可以使用len()cap()函数访问这些字段。

这里有一个工作的入门套件,它可以读取和打印二进制文件;你需要改变inName字面值来引用系统上的一个小文件。

package main
import (
"fmt";
"os";
)
func main()
{
inName := "file-rw.bin";
inPerm :=  0666;
inFile, inErr := os.Open(inName, os.O_RDONLY, inPerm);
if inErr == nil {
inBufLen := 16;
inBuf := make([]byte, inBufLen);
n, inErr := inFile.Read(inBuf);
for inErr == nil {
fmt.Println(n, inBuf[0:n]);
n, inErr = inFile.Read(inBuf);
}
}
inErr = inFile.Close();
}

这是一个很好的版本:

package main


import (
"io/ioutil";
)




func main() {
contents,_ := ioutil.ReadFile("plikTekstowy.txt")
println(string(contents))
ioutil.WriteFile("filename", contents, 0644)
}

让我们列出一个与Go 1兼容的列表,其中包括在Go中读写文件的所有方法。

因为文件API最近发生了变化,大多数其他答案都不适合Go 1。他们也错过了bufio,这在我看来很重要。

在下面的例子中,我通过从文件中读取并写入目标文件来复制一个文件。

从基础开始

package main


import (
"io"
"os"
)


func main() {
// open input file
fi, err := os.Open("input.txt")
if err != nil {
panic(err)
}
// close fi on exit and check for its returned error
defer func() {
if err := fi.Close(); err != nil {
panic(err)
}
}()


// open output file
fo, err := os.Create("output.txt")
if err != nil {
panic(err)
}
// close fo on exit and check for its returned error
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()


// make a buffer to keep chunks that are read
buf := make([]byte, 1024)
for {
// read a chunk
n, err := fi.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}


// write a chunk
if _, err := fo.Write(buf[:n]); err != nil {
panic(err)
}
}
}

这里我使用了os.Openos.Create,它们是os.OpenFile的方便包装器。我们通常不需要直接调用OpenFile

注意处理EOF。Read尝试在每次调用时填充buf,并在这样做时到达文件末尾时返回io.EOF作为错误。在这种情况下,buf仍将保存数据。对Read的后续调用返回0作为读取的字节数和相同的io.EOF作为错误。任何其他错误都会导致恐慌。

使用bufio

package main


import (
"bufio"
"io"
"os"
)


func main() {
// open input file
fi, err := os.Open("input.txt")
if err != nil {
panic(err)
}
// close fi on exit and check for its returned error
defer func() {
if err := fi.Close(); err != nil {
panic(err)
}
}()
// make a read buffer
r := bufio.NewReader(fi)


// open output file
fo, err := os.Create("output.txt")
if err != nil {
panic(err)
}
// close fo on exit and check for its returned error
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()
// make a write buffer
w := bufio.NewWriter(fo)


// make a buffer to keep chunks that are read
buf := make([]byte, 1024)
for {
// read a chunk
n, err := r.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}


// write a chunk
if _, err := w.Write(buf[:n]); err != nil {
panic(err)
}
}


if err = w.Flush(); err != nil {
panic(err)
}
}

bufio在这里只是充当缓冲区,因为我们与数据没有太多关系。在大多数其他情况下(特别是文本文件),bufio非常有用,它为我们提供了一个不错的API,以便轻松灵活地读写,同时它在幕后处理缓冲。


注意:以下代码适用于较旧的Go版本(Go 1.15及之前)。事情已经变了。对于新的方法,看一下这个答案

使用ioutil

package main


import (
"io/ioutil"
)


func main() {
// read the whole file at once
b, err := ioutil.ReadFile("input.txt")
if err != nil {
panic(err)
}


// write the whole body at once
err = ioutil.WriteFile("output.txt", b, 0644)
if err != nil {
panic(err)
}
}

易如反掌!但只有在确定处理的不是大文件时才使用它。

Read方法接受一个字节参数,因为这是它将读入的缓冲区。它在某些圈子中是常见的成语,当你思考它时,它是有意义的。

通过这种方式,您可以确定读取器将读取多少字节,并检查返回值以查看实际读取了多少字节,并适当地处理任何错误。

正如其他人在他们的回答中指出的那样,bufio可能是您从大多数文件读取所需要的。

我再加一个提示,因为它真的很有用。从文件中读取一行的最好方法不是使用ReadLine方法,而是使用ReadBytes或ReadString方法。

使用io.Copy

package main


import (
"io"
"log"
"os"
)


func main () {
// open files r and w
r, err := os.Open("input.txt")
if err != nil {
panic(err)
}
defer r.Close()


w, err := os.Create("output.txt")
if err != nil {
panic(err)
}
defer w.Close()


// do the actual work
n, err := io.Copy(w, r)
if err != nil {
panic(err)
}
log.Printf("Copied %v bytes\n", n)
}

如果你不喜欢重新发明轮子,io.Copyio.CopyN可以很好地为你服务。如果你检查来源的io。复制函数,它只是打包在Go库中的Mostafa的解决方案之一(实际上是“基本的”解决方案)。不过,他们使用的缓冲区比他大得多。

使用较新的Go版本,读取/写入文件或从文件中写入文件很容易。从文件中读取:

package main


import (
"fmt"
"io/ioutil"
)


func main() {
data, err := ioutil.ReadFile("text.txt")
if err != nil {
return
}
fmt.Println(string(data))
}

写入文件:

package main


import "os"


func main() {
file, err := os.Create("text.txt")
if err != nil {
return
}
defer file.Close()


file.WriteString("test\nhello")
}

这将覆盖文件的内容(如果文件不存在,则创建一个新文件)。

新方法

从Go 1.16开始,使用操作系统。ReadFile将文件加载到内存中,并使用os.WriteFile从内存写入文件(ioutil。ReadFile现在调用os.ReadFile)。

要小心os.ReadFile,因为它将整个文件读入内存。

package main


import "os"


func main() {
b, err := os.ReadFile("input.txt")
if err != nil {
log.Fatal(err)
}


// `b` contains everything your file has.
// This writes it to the Standard Out.
os.Stdout.Write(b)


// You can also write it to a file as a whole.
err = os.WriteFile("destination.txt", b, 0644)
if err != nil {
log.Fatal(err)
}
}

你也可以使用fmt包:

package main


import "fmt"


func main(){
file, err := os.Create("demo.txt")
if err != nil {
panic(err)
}
defer file.Close()
    

fmt.Fprint(file, name)
}