TrumanWong

Golang文件读写

TrumanWong
8/8/2023

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

文件读取

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

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

  1. 整块读取文件
  2. 按字节读取文件
  3. 逐行读取文件

整块读取文件

代码如下:

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语言中,写入文件的常用方式有以下三种:

  1. 整块写入文件
  2. 按字节写入文件
  3. 逐行写入文件

整块写入文件

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

代码如下:

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