7-常用标准库之-IO操作-文件写
一 打开文件
1.1 函数介绍
os.OpenFile()
函数能够以指定模式打开文件,从而实现文件写入相关功能。
func OpenFile(name string, flag int, perm FileMode) (*File, error) {}
func Create(name string) (*File, error) {}
//本质还是OpenFile--》return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
1.2 flag参数介绍
模式 | 含义 |
---|---|
os.O_WRONLY | 只写 |
os.O_CREATE | 创建文件 |
os.O_RDONLY | 只读 |
os.O_RDWR | 读写 |
os.O_TRUNC | 清空 |
os.O_APPEND | 追加 |
1.3 扩展阅读文件权限-perm介绍
# 在linux终端里使用ls -l查看文件权限操作权限
# 关于- rwx rwx rwx:
第一个 - :代表这是一个普通文件(regular), 其中其他文件类型还包括了
d--------->目录文件(directory)
l --------->链接文件(link)
b--------->块设备文件(block)
c--------->字符设备文件(character)
s--------->套接字文件(socket)
p--------->管道文件(pipe)
# 一共有三组rwx(下图中的每一个框起来的rwx对应一组用户权限),分别是以下几种用户的rwx权限:
第一组:该文件拥有者的权限
第二组:该文件拥有者所在组的其他成员对该文件的操作权限
第三组:其他用户组的成员对该文件的操作权限
# r表示读权限,w表示写权限,x表示执行权限
# 值是使用八进制进行计算的;例如:-rwxrwxrwx(前面的-代表这是一个普通文件)文件权限,计算过程如下:
rwx rwx rwx
111 111 111
7 7 7
此时该文件的权限值为777
# 其他
777 代表
该文件拥有者对该文件拥有读写操作的权限
该文件拥有者所在组的其他成员对该文件拥有读写操作的权限
其他用户组的成员对该文件也拥有读写操作权限
666代表
该文件拥有者对该文件拥有读写的权限但是没有操作的权限
该文件拥有者所在组的其他成员对该文件拥有读写的权限但是没有操作的权限
其他用户组的成员对该文件也拥有读写权限但是没有操作的权限
# 所以
r(读)04,w(写)02,x(执行)01
二 写入文件
2.1 File.Write(将字节写入文件)
func main() {
file,err:=os.Create("lqz.txt")
if err != nil {
fmt.Println("打开文件错误:",err)
return
}
defer file.Close()
//d2 := []byte{104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100}
//d2 := []byte("hello world")
d2 := []byte{'h','e','l','l','o'}
n,err:=file.Write(d2)
if err != nil {
fmt.Println("写入文件出错:",err)
}
fmt.Printf("%d字节写入文件成功",n)
}
2.2 File.WriteString(将字符串写入文件)
func main() {
file,err:=os.Create("lqz.txt")
if err != nil {
fmt.Println("打开文件错误:",err)
return
}
defer file.Close()
s:="hello world"
n,err:=file.WriteString(s)
if err != nil {
fmt.Println("写入文件出错:",err)
}
fmt.Printf("%d字节写入文件成功",n)
}
2.3 ioutil.WriteFile写入文件
func main() {
err:=ioutil.WriteFile("lqz.txt",[]byte("lqz is nb"),0666)
if err != nil {
fmt.Println("写入文件出错",err)
return
}
}
2.4 bufio.NewWriter写入文件
func main() {
file,err:=os.OpenFile("lqz.txt",os.O_CREATE|os.O_WRONLY,0666)
if err != nil {
fmt.Println("打开文件出错:",err)
return
}
defer file.Close()
writer:=bufio.NewWriter(file)
writer.WriteString("lqz is handsome")
writer.Flush() // 从缓冲区写入硬盘
}
三 其他
3.1 一行行写入到文件
func main() {
file,err:=os.OpenFile("lqz.txt",os.O_CREATE|os.O_WRONLY,0666)
if err != nil {
fmt.Println("打开文件出错:",err)
return
}
defer file.Close()
d := []string{"Welcome to the world of Go", "Go is a compiled language.", "It is easy to learn Go."}
for _, v := range d {
fmt.Fprintln(file, v)
if err != nil {
fmt.Println(err)
return
}
}
}
3.1 追加到文件
// 只要打开文件:file,err:=os.OpenFile("lqz.txt",os.O_APPEND|os.O_WRONLY,0666)
func main() {
file,err:=os.OpenFile("lqz.txt",os.O_APPEND|os.O_WRONLY,0666)
if err != nil {
fmt.Println("打开文件出错:",err)
return
}
defer file.Close()
d := []string{"Welcome to the world of Go", "Go is a compiled language.", "It is easy to learn Go."}
for _, v := range d {
fmt.Fprintln(file, v)
if err != nil {
fmt.Println(err)
return
}
}
}
3.2 并发写文件
当多个 goroutines 同时(并发)写文件时,我们会遇到竞争条件(race condition)。因此,当发生同步写的时候需要一个 channel 作为一致写入的条件。
我们将写一个程序,该程序创建 100 个 goroutinues。每个 goroutinue 将并发产生一个随机数,届时将有 100 个随机数产生。这些随机数将被写入到文件里面。我们将用下面的方法解决这个问题 .
- 创建一个 channel 用来读和写这个随机数。
- 创建 100 个生产者 goroutine。每个 goroutine 将产生随机数并将随机数写入到 channel 里。
- 创建一个消费者 goroutine 用来从 channel 读取随机数并将它写入文件。这样的话我们就只有一个 goroutinue 向文件中写数据,从而避免竞争条件。
- 一旦完成则关闭文件。
我们开始写产生随机数的 produce
函数:
func produce(data chan int, wg *sync.WaitGroup) {
n := rand.Intn(999)
data <- n
wg.Done()
}
上面的方法产生随机数并且将 data
写入到 channel 中,之后通过调用 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
的文件。然后从 channel 中读取随机数并且写到文件中。一旦读取完成并且将随机数写入文件后,通过往 done
这个 cahnnel 中写入 true
来通知任务已完成。
下面我们写 main
函数,并完成这个程序。下面是我提供的完整程序:
package main
import (
"fmt"
"math/rand"
"os"
"sync"
)
// 生产者,每个生产者随机产生一个999内的数字
func producer(data chan int, wg *sync.WaitGroup) {
r := rand.Intn(999)
data <- r
wg.Done()
}
func consumer(data chan int, done chan bool) {
file, err := os.Create("concurrent") // 打开一个文件
if err != nil {
fmt.Println(err)
return
}
for d := range data {
_, err := fmt.Fprintln(file, d)
if err != nil {
fmt.Println("写入出错")
file.Close() // 关闭文件
done <- false // 成功标志中写入false
return
}
}
file.Close() // 在for循环外面关闭文件
done <- true
}
func main() {
var (
data = make(chan int)
done = make(chan bool)
wg sync.WaitGroup
)
// 启动1000个协程生产随机数
for i := 0; i < 1000; i++ {
wg.Add(1)
go producer(data,&wg)
}
// 开启消费者,往文件中写随机数
go consumer(data,done)
go func() { //在另一个协程中等待所有生产者完成,然后关闭数据信道,不能写在主协程中
wg.Wait()
close(data)
}()
//从done中取出数据
res:=<-done
if res{ // true表示顺利写完
fmt.Println("顺利写完")
}else {// false 表示写出问题
fmt.Println("写出问题")
}
}
main
函数创建写入和读取数据的 channel,创建 done
这个 channel,此 channel 用于消费者 goroutinue 完成任务之后通知 main
函数。创建 Waitgroup 的实例 wg
,用于等待所有生产随机数的 goroutine 完成任务。
使用 for
循环创建 100 个 goroutines。调用 waitgroup 的 wait()
方法等待所有的 goroutines 完成随机数的生成。然后关闭 channel。当 channel 关闭时,消费者 consume
goroutine 已经将所有的随机数写入文件, 将 true
写入 done
这个 channel 中,这个时候 main
函数解除阻塞并且打印 File written successfully
。