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查看文件权限操作权限
image-20220319200320489
# 关于- 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 个随机数产生。这些随机数将被写入到文件里面。我们将用下面的方法解决这个问题 .

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

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

func produce(data chan int, wg *sync.WaitGroup) {
    n := rand.Intn(999)
    data <- n
    wg.Done()
}

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

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

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

posted @ 2022-03-19 21:55  刘清政  阅读(87)  评论(0编辑  收藏  举报