我一直在尝试自学 Go,但在尝试读取和写入普通文件时遇到了困难。
我可以达到inFile, _ := os.Open(INFILE, 0, 0)
,但实际上获取文件的内容没有意义,因为 read 函数将 a[]byte
作为参数。
func (file *File) Read(b []byte) (n int, err Error)
让我们列出在 Go 中读取和写入文件的所有方式的 Go 1 兼容列表。
因为文件 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.Open
了os.Create
哪些是方便的包装器os.OpenFile
。我们通常不需要OpenFile
直接调用。
注意处理EOF。Read
尝试填写buf
每个调用,io.EOF
如果这样做到达文件末尾,则返回错误。在这种情况下buf
仍将保存数据。随后的调用Read
返回零作为读取的字节数,与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
在这里只是充当缓冲区,因为我们与数据没有太多关系。在大多数其他情况下(特别是文本文件) ,它为我们提供了一个很好的 API,可以轻松灵活地进行读写,同时它在后台处理缓冲,这bufio
非常有用。
注意:以下代码适用于较旧的 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)
}
}
非常简单!但只有在您确定不处理大文件时才使用它。
这是一个很好的版本:
package main
import (
"io/ioutil";
)
func main() {
contents,_ := ioutil.ReadFile("plikTekstowy.txt")
println(string(contents))
ioutil.WriteFile("filename", contents, 0644)
}
使用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.Copy
andio.CopyN
可能会很好地为您服务。如果您检查io.Copy 函数的来源,它只不过是封装在 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 开始,使用os.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 does
// 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)
}
}
[]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();
}
尝试这个:
package main
import (
"io";
)
func main() {
contents,_ := io.ReadFile("filename");
println(string(contents));
io.WriteFile("filename", contents, 0644);
}
你也可以使用这个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)
}
只需查看文档,您似乎应该只声明一个 []byte 类型的缓冲区并将其传递给 read,然后它将读取那么多字符并返回实际读取的字符数(以及一个错误)。
文档说
Read 从文件中读取最多 len(b) 个字节。它返回读取的字节数和错误(如果有)。EOF 由 err 设置为 EOF 的零计数发出信号。
那不行吗?
编辑:另外,我认为您可能应该使用在bufio包中声明的 Reader/Writer 接口,而不是使用os包。
Read 方法接受一个字节参数,因为这是它将读取到的缓冲区。在某些圈子中,这是一个常见的成语,当你考虑它时,它是有道理的。
通过这种方式,您可以确定读取器将读取多少字节并检查返回以查看实际读取了多少字节并适当地处理任何错误。
正如其他人在他们的答案中指出的那样, bufio 可能是您想要从大多数文件中读取的内容。
我将添加另一个提示,因为它真的很有用。从文件中读取一行最好不要使用 ReadLine 方法,而是使用 ReadBytes 或 ReadString 方法。