Golang文件读写

TrumanWong
8/8/2023
TrumanWong

Go语言中,一切均可视为文件,一切均可视为字节流。本文主要介绍文件读取/写入的几种方法。

文件读取

文件读取是所有编程语言中最常见的操作之一。在实际项目的开发中,需要将大量的配置信息放在配置文件中,而不是硬编码到程序中,这样就可以根据实际情况灵活地进行调整,方便项目的部署。

Go语言中,读取文件的常用方式有三种:

整块读取文件

代码如下:

package main

import (
	"log"
	"os"
)

func main() {
	content, err := os.ReadFile("example.txt")
	if err != nil {
		log.Fatal(err)
	}
	log.Println("文件内容:", string(content))
}
这种方法一般适用于文件不是很大的情况,如果文件过大,一次读取全部内容可能会导致内存占用过高,甚至内存溢出的情况。

按字节读取文件

代码如下:

package main

import (
	"bufio"
	"io"
	"log"
	"os"
)

func main() {
	file, err := os.Open("example.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	r := bufio.NewReader(file)
	buffer := make([]byte, 1024)
	for {
		n, err := r.Read(buffer)
		if err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}
        // 分字节读取文件时,最后打印的时候应该调用string(buffer[:n])而不是调用string(buffer)来获取读取到的值,否则可能会打印出额外的字节。
		log.Println("读取到的字节数:", n)
		log.Println("读取到的内容:", string(buffer[:n]))
	}
}
分字节读取可以一次读取指定数量的字节,然后进行相应的处理,再继续读取后续的文件内容,这样占用的内存将大大降低。bufio包实现了带缓存的I/O操作,它封装了一个io.Readerio.Writer对象,使其具有缓存和一些文本读写功能。把文件读取进缓冲之后再读取的时候就可以避免文件系统的I/O读写,从而提高速度。

逐行读取文件

代码如下:

package main

import (
	"bufio"
	"log"
	"os"
)

func main() {
	file, err := os.Open("example.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		log.Println("读取到的内容:", scanner.Text())
	}
}

文件写入

和文件读取操作类似,Go语言中,写入文件的常用方式有以下三种:

整块写入文件

当写入的内容不是很大时,最常见的写入方法就是将字符串整块写入到文件中,这种方法也是最简单的。

代码如下:

package main

import (
	"log"
	"os"
)

func main() {
	f, err := os.Create("example.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	n, err := f.WriteString("Hello, world!")
	if err != nil {
		log.Fatal(err)
	}
	log.Println("写入的字节数:", n)
}
os.Create方法不是以追加模式打开文件,如果将os.Create方法用于已存在的文件,则会将文件的内容清空。

按字节写入文件

将字符串整体写入文件虽然非常简单,但是如果要写入的内容非常大,那么可能会出现内存不足的问题。同样的,我们可以按字节写入文件,这样就相当于把大量内容分批写入到文件中。

代码如下:

package main

import (
	"log"
	"os"
)

func main() {
	f, err := os.Create("example.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	content := []byte("Hello, world!")
	i := 0
	byteLen := 1024
	for {
		if i*byteLen > len(content) {
			break
		}

		start, end := i*byteLen, (i+1)*byteLen
		if (i+1)*byteLen > len(content) {
			start, end = i*byteLen, len(content)
		}
		n, err := f.Write(content[start:end])
		if err != nil {
			log.Fatal(err)
		}
		i++
		log.Println("写入的字节数:", n)
	}
	log.Println("写入完成")
}
调用文件的Write方法分字节数写入时,由于内容的长度不一定就是每批字节数的整数倍,因此最后一个字节切片需要进行特殊处理,否则写入的内容可能有额外的字节。

逐行写入文件

代码如下:

package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	f, err := os.Create("example.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	arr := []string{
		"Hello World",
		"Hello Golang",
	}

	for _, line := range arr {
		n, err := fmt.Fprintln(f, line)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("写入的字节数:", n)
		log.Println("写入的内容:", line)
	}
	log.Println("写入完成")
}
追加内容到文件中可以使用f, err := os.OpenFile("example.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)

文中代码已上传至Github