over-golang/01-基础语法/13-文件操作-2-读操作.md
2021-07-02 18:11:59 +08:00

4.7 KiB
Raw Permalink Blame History

一 文件读取

文件读写的接口位于io包file文件类是这些接口的实现类。

1.1 直接读取 read()

read() 实现的是按字节数读取:

	readByte := make([]byte, 128)			// 指定要读取的长度
	for {
		n, err := f.Read(readByte)			// 将数据读取如切片,返回值 n 是实际读取到的字节数
		if err != nil && err != io.EOF{		// 如果读到了文件末尾EOF 即 end of file
			fmt.Println("read file : ", err)
			break
		}

		fmt.Println("read: ", string(readByte[:n]))
		if n < 128 {
			fmt.Println("read end")
			break
		}
	}

1.2 bufio的写操作

bufio封装了io.Reader、io.Writer接口对象并创建了另一个也实现了该接口的对象bufio.Reader、bufio.Writer。通过该实现bufio实现了文件的缓冲区设计可以大大提高文件I/O的效率。

使用bufio读取文件时先将数据读入内存的缓冲区缓冲区一般比要比程序中设置的文件接收对象要大这样就可以有效降低直接I/O的次数。

bufio.Read([]byte)相当于读取大小len(p)的内容:

  • 当缓冲区有内容时将缓冲区内容全部填入p并清空缓冲区
  • 当缓冲区没有内容且len(p)>len(buf),即要读取的内容比缓冲区还要大,直接去文件读取即可
  • 当缓冲区没有内容且len(p)<len(buf)即要读取的内容比缓冲区小读取文件内容并填满缓冲区并将p填满
  • 以后再次读取时缓冲区有内容将缓冲区内容全部填入p并清空缓冲区和第一步一致

示例:

	// 创建读对象
	reader := bufio.NewReader(f)

	// 读一行数据
	byt, _ := reader.ReadBytes('\n')			
	fmt.Println(string(byt))

ReadString() 函数也具有同样的功能,且能直接读取到字符串数据,无需转换,示例:读取大文件的全部数据

	reader := bufio.NewReader(f)
	for {										// 按照缓冲区读取:读取到特定字符结束
		str, err := reader.ReadString('\n')		// 按行读取
		if err != nil && err != io.EOF {
			fmt.Println("read err: ", err)
			break
		}
		fmt.Println("str = ", str)
		if err == io.EOF {
			fmt.Print("read end")
			break
		}
	}

在Unix设计思想中一切皆文件命令行输入也可以作为文件读入

	reader := bufio.NewReader(os.Stdin)
	s, _ := reader.ReadString("-")		// 假设命令行以 - 开始

缓冲的思想通过bufio数据被写入用户缓冲再进入系统缓冲最后由操作系统将系统缓冲区的数据写入磁盘。

1.3 io/ioutil 包文件读取

ioutil直接读取文件

	ret, err := ioutil.ReadFile("test.txt")
	if err != nil {
		fmt.Println("read err :", err)
		return
	}
	fmt.Println(string(ret))

二 文件写入

2.1 直接写

	f, err := os.OpenFile("test.txt", os.O_CREATE | os.O_WRONLY, os.ModePerm)
	if err != nil {
		fmt.Println("open err:", err)
		return
	}
	defer f.Close()

	n, err := f.Write([]byte("hello world"))
	if err != nil {
		fmt.Println("write err:", err)
	}
	fmt.Println(n)								// 每次都会从头开始重新写入

上述案例中,如果我们不想每次写入都会从头开始重新写入,那么需要将打开模式修改为:os.O_CREATE | os.O_WRONLY | os.O_APPEND

2.2 bufio的写操作

	writer := bufio.NewWriter(f)
	_, err = writer.WriteString("hello world!")
	if err != nil {
		fmt.Println("write err:", err)
		return
	}
	writer.Flush()		// 必须刷新缓冲区:将缓冲区的内容写入文件中。如果不刷新,则只会在内容超出缓冲区大小时写入

2.3 io/ioutil 包文件写入

	s := "你好世界"
	err := ioutil.WriteFile("test.txt", []byte(s), os.ModePerm)

三 文件读取偏移量

文件读取时,是可以控制光标位置的:

	f, err := os.OpenFile("test.txt", os.O_RDWR, os.ModePerm)
	if err != nil {
		fmt.Println("open err:", err)
		return
	}
	defer f.Close()

	// 读取前五个字节,假设读取的文件内容为: hello world!
	bs := []byte{0}		// 创建1个字节的切片
	_, err = f.Read(bs)
	if err != nil {
		fmt.Println("read err:", err)
		return
	}
	fmt.Println("读到的数据是:", string(bs))	// h

	// 移动光标
	_, err = f.Seek(4, io.SeekStart)		// 光标从开始位置(h之前)移动4位到达o之前
	if err != nil {
		fmt.Println("seek err:", err)
		return
	}
	_, err = f.Read(bs)
	if err != nil {
		fmt.Println("read err:", err)
		return
	}
	fmt.Println("读到的数据是:", string(bs))		// o

通过记录光标的位置可以实现断点续传假设已经下载了1KB文件即本地临时文件存储了1KB此时断电重启后通过本地文件大小、Seek()方法获取到上次读取文件的光标位置即可实现继续下载!