在本教程中,我们将学习如何使用 Go 将数据写入文件。我们还将学习如何同时向一个文件写入数据。
本教程包含以下部分
- 将字符串写入文件
- 将字节写入文件
- 一行一行将数据写入文件
- 添加到文件
- 并发写入文件
将字符串写入文件
最常见的文件写入操作之一是将字符串写入文件。这很简单。它包括以下步骤。
- 创建文件
- 将字符串写入文件
让我们马上看看代码吧。
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Create("test.txt")
if err != nil {
fmt.Println(err)
return
}
l, err := f.WriteString("Hello World")
if err != nil {
fmt.Println(err)
f.Close()
return
}
fmt.Println(l, "bytes written successfully")
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
}
上面的程序第 9 行 create
函数创建一个名为 test.txt 的文件。 如果已存在具有该名称的文件,则 create 函数会截断该文件。此函数返回文件描述符。
第 14 行我们使用 WriteString
方法将字符串 Hello World 写入文件。 此方法返回写入的字节数和 error (如果有错误)。
最后在 21 行我们关闭文件
上面的程序将输出
11 bytes written successfully
你可以在执行此程序的目录中找到名为 test.txt 的文件。如果使用任何文本编辑器打开文件,则可以发现它包含文本 Hello World。
将字节写入文件
将字节写入文件与将字符串写入文件非常相似。我们将使用 Write 方法将字节写入文件。以下程序将一片字节写入文件。
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Create("/home/naveen/bytes")
if err != nil {
fmt.Println(err)
return
}
d2 := []byte{104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100}
n2, err := f.Write(d2)
if err != nil {
fmt.Println(err)
f.Close()
return
}
fmt.Println(n2, "bytes written successfully")
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
}
在上面的程序中,第 15 行我们使用 Write 方法将一片字节写入目录 /home/naveen
中名为 bytes
的文件。 你可以将此目录更改为其他目录。 剩下的程序是一目了然的。 该程序将打印 bytes written successfully
,并创建一个名为 bytes
的文件。 打开文件,你可以看到它包含文本 hello bytes
将字符串逐行写入文件
另一个常见的文件操作是需要逐行将字符串写入文件。在本节中,我们将编写一个程序来创建具有以下内容的文件。
Welcome to the world of Go.
Go is a compiled language.
It is easy to learn Go.
让我们马上看看代码吧。
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Create("lines")
if err != nil {
fmt.Println(err)
f.Close()
return
}
d := []string{"Welcome to the world of Go1.", "Go is a compiled language.", "It is easy to learn Go."}
for _, v := range d {
fmt.Fprintln(f, v)
if err != nil {
fmt.Println(err)
return
}
}
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
fmt.Println("file written successfully")
}
程序第 9 行,我们创建一个名为 lines 的新文件。第 17 行我们使用 for range 循环遍历数组,并使用 Fprintln 函数将行写入文件。 Fprintln 函数将 io.writer 作为参数并添加一个新行,正如我们想的那样。 运行此程序将打印 file written successfully
,并在当前目录中创建文件 lines
。 文件 lines
的内容如下所示。
Welcome to the world of Go1.
Go is a compiled language.
It is easy to learn Go.
对文件进行追加
在本节中,我们将在我们在上一节中创建的 lines
文件中再添加一行。我们将添加 File handling is easy 到 lines
文件。
必须以追加和只写模式打开文件。传递这些标志的参数传递给 Open 函数。在追加模式下打开文件后,我们将新行添加到文件中。
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.OpenFile("lines", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Println(err)
return
}
newLine := "File handling is easy."
_, err = fmt.Fprintln(f, newLine)
if err != nil {
fmt.Println(err)
f.Close()
return
}
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
fmt.Println("file appended successfully")
}
第 9 行我们以追加和只写模式打开文件。 文件成功打开后,我们第 15 行在文件中添加新行。 该程序将打印 file appended successfully
。 运行此程序后,lines
文件的内容将是,
Welcome to the world of Go1.
Go is a compiled language.
It is easy to learn Go.
File handling is easy.
并发写入文件
当多个 goroutine 同时写入文件时,我们最终会遇到竞争条件。 因此,应该使用信道协调对文件的并发写入。
我们将编写一个创建 100 个 goroutines 的程序。 每个 goroutine 将同时生成一个随机数,从而总共生成一百个随机数。 这些随机数将写入文件。 我们将使用以下方法解决此问题。
- 创建一个信道,用于读取和写入生成的随机数。
- 创建 100 个生产者goroutines。每个 goroutine 将生成一个随机数,并将该随机数写入一个信道。
- 创建一个消费者 goroutine,它将从信道读取并将生成的随机数写入文件。因此,我们只有一个 goroutine 并发地写入文件,从而避免了竞争条件:)
- 一旦完成,关闭文件。
我们先写一下生成随机数的 produce
函数。
func produce(data chan int, wg *sync.WaitGroup) {
n := rand.Intn(999)
data <- n
wg.Done()
}
上面的函数生成一个随机数并将其写入 data
信道,然后调用 waitgroup 上的 Done
函数,通知它已经完成了任务。
现在让我们转到写入文件的函数。
func consume(data chan int, done chan bool) {
f, err := os.Create("concurrent")
if err != nil {
fmt.Println(err)
return
}
for d := range data {
_, err = fmt.Fprintln(f, d)
if err != nil {
fmt.Println(err)
f.Close()
done <- false
return
}
}
err = f.Close()
if err != nil {
fmt.Println(err)
done <- false
return
}
done <- true
}
consume
函数创建一个名为 concurrent
的文件。 然后它从 data
信道读取随机数并写入文件。 一旦它读取并写入了所有随机数,它就会向 done
信道写入 true
,以通知它完成了它的任务。
让我们编写 main
函数并完成该程序。 我在下面提供了整个程序。
package main
import (
"fmt"
"math/rand"
"os"
"sync"
)
func produce(data chan int, wg *sync.WaitGroup) {
n := rand.Intn(999)
data <- n
wg.Done()
}
func consume(data chan int, done chan bool) {
f, err := os.Create("concurrent")
if err != nil {
fmt.Println(err)
return
}
for d := range data {
_, err = fmt.Fprintln(f, d)
if err != nil {
fmt.Println(err)
f.Close()
done <- false
return
}
}
err = f.Close()
if err != nil {
fmt.Println(err)
done <- false
return
}
done <- true
}
func main() {
data := make(chan int)
done := make(chan bool)
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go produce(data, &wg)
}
go consume(data, done)
go func() {
wg.Wait()
close(data)
}()
d := <-done
if d == true {
fmt.Println("File written successfully")
} else {
fmt.Println("File writing failed")
}
}
main 函数在第 41 行创建从中读取和写入随机数的 data
信道。 consume
goroutine 使用第 42 行的 done
信道来通知 main
它完成了它的任务。第 43 行 wg
waitgroup用于等待所有 100 个 goroutine 完成生成随机数。
for
循环创建 100 个 goroutines。goroutine 调用 49 行 waitgroup 上的 wait()
等待所有 100 个 goroutine 完成创建随机数。 之后它关闭了信道。 一旦信道关闭并且consume
goroutine 已经完成将所有生成的随机数写入文件,第 37 行它将 true
写入 done
信道并且主 goroutine 被解除阻塞并打印 File written successfully。
现在你可以在任何文本编辑器中打开文件 concurrent ,并查看 100 个生成的随机数:)