欢迎来到 Golang 系列教程的第 36 篇。

在这一章我们将学习如何使用 Go 语言将数据写到文件里面。并且还要学习如何同步的写到文件里面。

这章教程包括如下几个部分:

  • 将字符串写入文件
  • 将字节写入文件
  • 将数据一行一行的写入文件
  • 追加到文件里
  • 并发写文件

请在本地运行所有本教程的程序,因为 playground 对文件的操作支持的并不好。

将字符串写入文件

最常见的写文件就是将字符串写入文件。这个写起来非常的简单。这个包含以下几个阶段。

  1. 创建文件
  2. 将字符串写入文件

我们将得到如下代码。

在第 9 行使用 create 创建一个名字为 test.txt 的文件。如果这个文件已经存在,那么 create 函数将截断这个文件。该函数返回一个文件描述符

在第 14 行,我们使用 WriteString 将字符串 Hello World 写入到文件里面。这个方法将返回相应写入的字节数,如果有错误则返回错误。

最后,在第 21 行我们将文件关闭。

上面程序将打印:

11 bytes written successfully

运行完成之后你会在程序运行的目录下发现创建了一个 test.txt 的文件。如果你使用文本编辑器打开这个文件,你可以看到文件里面有一个 Hello World 的字符串。

将字节写入文件

将字节写入文件和写入字符串非常的类似。我们将使用 Write 方法将字节写入到文件。下面的程序将一个字节的切片写入文件。

在上面的程序中,第 15 行使用了 Write 方法将字节切片写入到 bytes 这个文件里。这个文本在目录 /home/naveen 里面。你也可以将这个目录换成其他的目录。剩余的程序自带解释。如果执行成功,这个程序将打印 11 bytes written successfully。并且创建一个 bytes 的文件。打开文件,你会发现该文件包含了文本 hello bytes

将字符串一行一行的写入文件

另外一个常用的操作就是将字符串一行一行的写入到文件。这一部分我们将写一个程序,该程序创建并写入如下内容到文件里。

Welcome to the world of Go.Go is a compiled language.It is easy to learn Go.

让我们看下面的代码:

在上面程序的第 9 行,我们先创建一个名字叫做 lines 的文件。在第 17 行,我们用迭代并使用 for rang 循环这个数组,并使用 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 方法实现。当文件以追加的方式打开,我们添加新的行到文件里。

在上面程序的第 9 行,我们以写的方式打开文件并将一行添加到文件里。当成功打开文件之后,在程序第 15 行,我们添加一行到文件里。程序成功将打印 file appended successfully。运行程序,新的行就加到文件里面去了。

Welcome to the world of Go1.Go is a compiled language.It is easy to learn Go.File handling is easy.
并发写文件

当多个 goroutines 同时(并发)写文件时,我们会遇到竞争条件(race condition)。因此,当发生同步写的时候需要一个 channel 作为一致写入的条件。

我们将写一个程序,该程序创建 100 个 goroutinues。每个 goroutinue 将并发产生一个随机数,届时将有 100 个随机数产生。这些随机数将被写入到文件里面。我们将用下面的方法解决这个问题 .

  1. 创建一个 channel 用来读和写这个随机数。
  2. 创建 100 个生产者 goroutine。每个 goroutine 将产生随机数并将随机数写入到 channel 里。
  3. 创建一个消费者 goroutine 用来从 channel 读取随机数并将它写入文件。这样的话我们就只有一个 goroutinue 向文件中写数据,从而避免竞争条件。
  4. 一旦完成则关闭文件。

我们开始写产生随机数的 produce 函数:

上面的方法产生随机数并且将 data 写入到 channel 中,之后通过调用 waitGroup 的 Done 方法来通知任务已经完成。

让我们看看将数据写到文件的函数:

这个 consume 的函数创建了一个名为 concurrent 的文件。然后从 channel 中读取随机数并且写到文件中。一旦读取完成并且将随机数写入文件后,通过往 done 这个 cahnnel 中写入 true 来通知任务已完成。

下面我们写 main 函数,并完成这个程序。下面是我提供的完整程序:

main 函数在第 41 行创建写入和读取数据的 channel,在第 42 行创建 done 这个 channel,此 channel 用于消费者 goroutinue 完成任务之后通知 main 函数。第 43 行创建 Waitgroup 的实例 wg,用于等待所有生产随机数的 goroutine 完成任务。

在第 44 行使用 for 循环创建 100 个 goroutines。在第 49 行调用 waitgroup 的 wait() 方法等待所有的 goroutines 完成随机数的生成。然后关闭 channel。当 channel 关闭时,消费者 consume goroutine 已经将所有的随机数写入文件,在第 37 行 将 true 写入 done 这个 channel 中,这个时候 main 函数解除阻塞并且打印 File written successfully。

现在你可以用任何的文本编辑器打开文件 concurrent,可以看到 100 个随机数已经写入

本教程到此结束。希望你能喜欢,祝你愉快。

上一教程 - 「GCTT 出品」Go 系列教程——35. 读取文件

历史文章:

「GCTT 出品」Go 系列教程——1. 介绍与安装

「GCTT 出品」Go 系列教程——2. Hello World

「GCTT 出品」Go 系列教程——3. 变量

「GCTT 出品」Go 系列教程——4. 类型

「GCTT 出品」Go 系列教程——5. 常量

「GCTT 出品」Go 系列教程——6. 函数(Function)

「GCTT 出品」Go 系列教程——7. 包

Go 系列教程——8. if-else 语句

「GCTT 出品」Go 系列教程——9. 循环

「GCTT 出品」Go 系列教程——10. switch 语句

「GCTT 出品」Go 系列教程——11. 数组和切片

「GCTT 出品」Go 系列教程——12. 可变参数函数

「GCTT 出品」Go 系列教程——13. Maps

「GCTT 出品」Go 系列教程——14. 字符串

「GCTT 出品」Go 系列教程——15. 指针

「GCTT 出品」Go 系列教程——16. 结构体,这一篇就够

「GCTT 出品」Go 系列教程——17. 超全的方法教程

「GCTT 出品」Go 系列教程——18. 接口(一)

「GCTT 出品」Go 系列教程——19. 接口(二)

「GCTT 出品」Go 系列教程——20. 并发入门

「GCTT 出品」Go 系列教程——21. Go 协程

「GCTT 出品」Go 系列教程——22. 信道(channel)

「GCTT 出品」Go 系列教程——23. 缓冲信道和工作池

「GCTT 出品」Go 系列教程——24. Select

「GCTT 出品」Go 系列教程——25. Mutex

「GCTT 出品」Go 系列教程——26. 结构体取代类

「GCTT 出品」Go 系列教程——27. 组合取代继承

「GCTT 出品」Go 系列教程——28. 多态

「GCTT 出品」Go 系列教程——29. Defer

「GCTT 出品」Go 系列教程——30. 错误处理

「GCTT 出品」Go 系列教程——31. 自定义错误

「GCTT 出品」Go 系列教程——32. panic 和 recover

「GCTT 出品」Go 系列教程——33. 函数是一等公民(头等函数)

「GCTT 出品」Go 系列教程——34. 反射

「GCTT 出品」Go 系列教程——35. 读取文件