英文:
How to read/write from/to a file using Go
问题
我一直在尝试自学Go语言,但是在尝试从普通文件中读取和写入时遇到了困难。
我可以做到inFile, _ := os.Open(INFILE, 0, 0)
,但是实际上获取文件的内容没有意义,因为读取函数需要一个[]byte
作为参数。
func (file *File) Read(b []byte) (n int, err Error)
英文:
I've been trying to learn Go on my own, but I've been stumped on trying read from and write to ordinary files.
I can get as far as inFile, _ := os.Open(INFILE, 0, 0)
, but actually getting the content of the file doesn't make sense, because the read function takes a []byte
as a parameter.
func (file *File) Read(b []byte) (n int, err Error)
答案1
得分: 560
让我们制作一个与Go 1兼容的文件读写方式的列表。
由于文件API最近发生了变化,大多数其他答案在Go 1中不起作用。它们还缺少我认为很重要的bufio
。
在以下示例中,我通过从输入文件读取并写入目标文件来复制文件。
从基础开始
package main
import (
"io"
"os"
)
func main() {
// 打开输入文件
fi, err := os.Open("input.txt")
if err != nil {
panic(err)
}
// 在退出时关闭fi并检查其返回的错误
defer func() {
if err := fi.Close(); err != nil {
panic(err)
}
}()
// 打开输出文件
fo, err := os.Create("output.txt")
if err != nil {
panic(err)
}
// 在退出时关闭fo并检查其返回的错误
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()
// 创建一个缓冲区以保存读取的块
buf := make([]byte, 1024)
for {
// 读取一个块
n, err := fi.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}
// 写入一个块
if _, err := fo.Write(buf[:n]); err != nil {
panic(err)
}
}
}
在这里,我使用了os.Open
和os.Create
,它们是对os.OpenFile
的方便包装。通常我们不需要直接调用OpenFile
。
注意处理EOF。Read
在每次调用时尝试填充buf
,如果在此过程中达到文件末尾,则返回io.EOF
作为错误。在这种情况下,buf
仍将保存数据。后续调用Read
将返回读取的字节数为零,并返回相同的io.EOF
错误。任何其他错误都会导致恐慌。
使用bufio
package main
import (
"bufio"
"io"
"os"
)
func main() {
// 打开输入文件
fi, err := os.Open("input.txt")
if err != nil {
panic(err)
}
// 在退出时关闭fi并检查其返回的错误
defer func() {
if err := fi.Close(); err != nil {
panic(err)
}
}()
// 创建一个读取缓冲区
r := bufio.NewReader(fi)
// 打开输出文件
fo, err := os.Create("output.txt")
if err != nil {
panic(err)
}
// 在退出时关闭fo并检查其返回的错误
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()
// 创建一个写入缓冲区
w := bufio.NewWriter(fo)
// 创建一个缓冲区以保存读取的块
buf := make([]byte, 1024)
for {
// 读取一个块
n, err := r.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}
// 写入一个块
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
自Go 1.16起已被弃用)。有关新方法,请参阅此答案。
使用ioutil
package main
import (
"io/ioutil"
)
func main() {
// 一次性读取整个文件
b, err := ioutil.ReadFile("input.txt")
if err != nil {
panic(err)
}
// 一次性写入整个内容
err = ioutil.WriteFile("output.txt", b, 0644)
if err != nil {
panic(err)
}
}
简单易行!但只在确定不处理大文件时使用。
英文:
Let's make a Go 1-compatible list of all the ways to read and write files in Go.
Because file API has changed recently and most other answers don't work with Go 1. They also miss bufio
which is important IMHO.
In the following examples I copy a file by reading from it and writing to the destination file.
Start with the basics
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)
}
}
}
Here I used os.Open
and os.Create
which are convenient wrappers around os.OpenFile
. We usually don't need to call OpenFile
directly.
Notice treating EOF. Read
tries to fill buf
on each call, and returns io.EOF
as error if it reaches end of file in doing so. In this case buf
will still hold data. Consequent calls to Read
returns zero as the number of bytes read and same io.EOF
as error. Any other error will lead to a panic.
Using 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
is just acting as a buffer here, because we don't have much to do with data. In most other situations (specially with text files) bufio
is very useful by giving us a nice API for reading and writing easily and flexibly, while it handles buffering behind the scenes.
Note: The following code is for older Go versions (Go 1.15 and before). Things have changed (ioutil
is deprecated since Go 1.16). For the new way, take a look at this answer.
Using 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)
}
}
Easy as pie! But use it only if you're sure you're not dealing with big files.
答案2
得分: 57
这是一个好的版本(但请注意,自Go 1.16起,ioutil
已被弃用):
package main
import (
"io/ioutil";
)
func main() {
contents,_ := ioutil.ReadFile("plikTekstowy.txt")
println(string(contents))
ioutil.WriteFile("filename", contents, 0644)
}
英文:
This is a good version (but do note that ioutil
is deprecated since Go 1.16):
package main
import (
"io/ioutil";
)
func main() {
contents,_ := ioutil.ReadFile("plikTekstowy.txt")
println(string(contents))
ioutil.WriteFile("filename", contents, 0644)
}
答案3
得分: 43
新的方法
从Go 1.16开始,使用os.ReadFile将文件加载到内存中,并使用os.WriteFile
从内存中写入文件(ioutil.ReadFile现在调用os.ReadFile
并已被弃用)。
请注意,os.ReadFile
会将整个文件读入内存。
package main
import "os"
import "log"
func main() {
b, err := os.ReadFile("input.txt")
if err != nil {
log.Fatal(err)
}
// `b` 包含了文件的全部内容。
// 这将其写入标准输出。
os.Stdout.Write(b)
// 你也可以将其作为一个整体写入文件。
err = os.WriteFile("destination.txt", b, 0644)
if err != nil {
log.Fatal(err)
}
}
英文:
New Way
Starting with Go 1.16, use os.ReadFile to load the file into memory, and use os.WriteFile
to write to a file from memory (ioutil.ReadFile now calls os.ReadFile
and is deprecated).
Be careful with the os.ReadFile
because it reads the whole file into memory.
package main
import "os"
import "log"
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)
}
}
答案4
得分: 41
使用io.Copy
package main
import (
"io"
"log"
"os"
)
func main() {
// 打开文件 r 和 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()
// 进行实际的操作
n, err := io.Copy(w, r)
if err != nil {
panic(err)
}
log.Printf("已复制 %v 字节\n", n)
}
如果你不想重复造轮子,io.Copy
和 io.CopyN
可能会很有用。如果你查看io.Copy函数的源代码,你会发现它实际上就是 Mostafa 的解决方案之一(实际上是“基本”解决方案),只是在 Go 库中进行了封装。不过他们使用的缓冲区比他使用的要大得多。
英文:
Using 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)
}
If you don't feel like reinventing the wheel, the io.Copy
and io.CopyN
may serve you well. If you check the source of the io.Copy function, it is nothing but one of the Mostafa's solutions (the 'basic' one, actually) packaged in the Go library. They are using a significantly larger buffer than he is, though.
答案5
得分: 24
注意。自Go 1.16起,ioutil
已被弃用。
使用更新的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")
}
这将覆盖文件的内容(如果文件不存在,则创建一个新文件)。
英文:
Note. ioutil
is deprecated since Go 1.16.
With newer Go versions, reading/writing to/from a file is easy. To read from a file:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
data, err := ioutil.ReadFile("text.txt")
if err != nil {
return
}
fmt.Println(string(data))
}
To write to a file:
package main
import "os"
func main() {
file, err := os.Create("text.txt")
if err != nil {
return
}
defer file.Close()
file.WriteString("test\nhello")
}
This will overwrite the content of a file (create a new file if it was not there).
答案6
得分: 10
[]byte
是一个字节数组的切片(类似于子字符串)。将切片视为一个值结构,其中包含一个隐藏的指针字段,用于系统定位和访问数组的全部或部分内容(切片),以及用于切片的长度和容量的字段,可以使用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();
}
英文:
[]byte
is a slice (similar to a substring) of all or part of a byte array. Think of the slice as a value structure with a hidden pointer field for the system to locate and access all or part of an array (the slice), plus fields for the length and capacity of the slice, which you can access using the len()
and cap()
functions.
Here's a working starter kit for you, which reads and prints a binary file; you will need to change the inName
literal value to refer to a small file on your system.
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();
}
答案7
得分: 7
注意。自Go 1.16起,ReadFile
在os
包中。
尝试这样做:
package main
import (
"io"
)
func main() {
contents,_ := io.ReadFile("filename")
println(string(contents))
io.WriteFile("filename", contents, 0644)
}
[a]: https://pkg.go.dev/os#ReadFile
英文:
Note. Since Go 1.16 ReadFile
is in the os
package.
Try this:
package main
import (
"io";
)
func main() {
contents,_ := io.ReadFile("filename");
println(string(contents));
io.WriteFile("filename", contents, 0644);
}
答案8
得分: 4
你也可以使用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)
}
英文:
you can use the fmt
package also:
package main
import "fmt"
func main(){
file, err := os.Create("demo.txt")
if err != nil {
panic(err)
}
defer file.Close()
fmt.Fprint(file, name)
}
答案9
得分: 1
只看文档,似乎你只需要声明一个类型为[]byte的缓冲区,并将其传递给read函数,该函数将读取多达指定数量的字符,并返回实际读取的字符数(以及错误)。
文档中说
> Read从文件中读取多达len(b)字节。它返回读取的字节数和错误(如果有)。当err设置为EOF时,通过计数为零来表示EOF。
这个方法不起作用吗?
编辑:此外,我认为你应该使用bufio包中声明的Reader/Writer接口,而不是使用os包。
英文:
Just looking at the documentation it seems you should just declare a buffer of type []byte and pass it to read which will then read up to that many characters and return the number of characters actually read (and an error).
The docs say
> Read reads up to len(b) bytes from the File. It returns the number of bytes read and an Error, if any. EOF is signaled by a zero count with err set to EOF.
Does that not work?
EDIT: Also, I think you should perhaps use the Reader/Writer interfaces declared in the bufio package instead of using os package.
答案10
得分: 1
Read方法接受一个字节参数,因为它将读取到该缓冲区中。这在某些圈子中是一种常见的习惯用法,当你思考一下的时候,它也是有道理的。
通过这种方式,你可以确定读取器将读取多少字节,并检查返回值以查看实际读取了多少字节,并适当处理任何错误。
正如其他人在他们的答案中指出的那样,对于从大多数文件中读取,bufio可能是你想要的。
我还要补充一个提示,因为它非常有用。从文件中读取一行最好不要使用ReadLine方法,而是使用ReadBytes或ReadString方法。
英文:
The Read method takes a byte parameter because that is the buffer it will read into. It's a common idiom in some circles and makes some sense when you think about it.
This way you can determine how many bytes will be read by the reader and inspect the return to see how many bytes actually were read and handle any errors appropriately.
As others have pointed in their answers, bufio is probably what you want for reading from most files.
I'll add one other hint since it's really useful. Reading a line from a file is best accomplished not by the ReadLine method, but the ReadBytes or ReadString method instead.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论