写入字符串
经常会遇到向文件中写入字符串的需求,实现起来很简单,包括如下两步:
Copy 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
}
}
通过os.Create
函数,创建了文件test.txt
,该函数返回了创建的文件的文件描述符。注意,如果要创建的文件名称已存在,存在的文件会被删除。我们通过WriteString
方法向文件中写入了字符串Hello World 你好 世界
,该方法返回写入的字符串的字节数。最后注意通过f.Close()
关闭文件。该程序执行输出:
Copy 25 bytes written successfully
同时生成一个文件test.txt
,包含文本Hello World 你好 世界
。
写入字节
向文件中写入字节跟写入字符串类似。使用Write
方法即可。下面的程序将一个切片的字节写入到文件中。
Copy package main
import (
"fmt"
"os"
)
func main () {
f, err := os. Create ( "bytes.txt" )
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
}
}
这里我们打开文件后,通过Write
方法将一个切片的字节写入文件。该程序执行输出:
Copy 11 bytes written successfully
同时创建了文件bytes.txt
,包含文本hello world
。
按行写入字符串
下面我们将如下几行字符串按行写入文件。
Copy Welcome to the world of Go.
Go is a compiled language.
It is easy to learn Go.
Copy package main
import (
"fmt"
"os"
)
func main () {
f, err := os. Create ( "lines.txt" )
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" )
}
这里我们还是通过os.Create()
来创建了一个文件。接着通过Fprintln
函数将一行一行的字符串写入到了文件中。最后关闭了文件。该程序执行输出:
Copy file written successfully
同时创建了文件lines.txt
,包含了三行文本。
追加写入字符串
下面我们将下面这行文本追加写入到前面创建的文件lines.txt
中。
Copy File handling is easy
要追加写入一个文件,该文件必须以追加模式打开。OpenFile
函数的参数可配置以哪种模式打开文件。
Copy package main
import (
"fmt"
"os"
)
func main () {
f, err := os. OpenFile ( "lines.txt" , 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" )
}
这里我们给函数OpenFile
传入了os.O_APPEND|os.O_WRONLY
表示追加+仅写模式。该程序执行输出:
Copy file appended successfully
同时在文件lines.txt
最后追加了一行文本File handling is easy.
。
并发写文件
如果多个goroutine
并发的向同一个文件写入,会出现竞态问题。这种情况下我们需要通过channel
,来将所有goroutine
要写入的数据做一个顺序化,跟排队类似。这本质上其实也是生产消费模型。
下面我们创建100个goroutine
,在每个goroutine
中并发的生成一个随机数,然后放入到channel
中。在channel
的接收端会收到来自各个goroutine
要写入的数据,然后依次无冲突的写入到文件中。
Copy 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.txt" )
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" )
}
}
这里的核心思想就是生产、消费模型,produce
协程函数用来生产数据,consume
协程用来消费数据。这里我们通过wg.Wait()
在所有生产者完成生产后调用close
关闭了channel
。同时我们在consume
协程中不断读取channel
中的数据,直到channel
被关闭。consume
协程完成按序写后调用f.Close()
关闭了文件。该程序执行输出为:
Copy File written successfully
同时创建了文件concurrent.txt
,包含100行随机数字。