Go语言中,文件的读写操作有很多种方式。比如以二进制字节码byte方式读取写入,亦或将整个文件的内容读入内存之类的方法。从实用角度上来说,为了避免文件过大把内存吃满,按行读取string字符串的方式最为常见。同样以string类型写入文件也是通用的方法。

创建一个txt文件

% cat config.txt 
113 334 556
113 324 589
115 331 557
456 213 509

按行读取文件方法一(bufio.NewReader)

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "strings" // 注意是strings包,不是string包
)

func main() {
    // 获取文件句柄
    f, err := os.Open("config.txt")
    // 最后要记得关闭文件
    defer func() {
        if err := f.Close(); err != nil {
            fmt.Println("文件打开错误")
        }
    }()
    
    if err != nil {
        fmt.Println(err)
    }

    // 实例化bufio接口,传入句柄文件
    buf := bufio.NewReader(f)

    for {
        // 循环读取文件内容,每次读取一行

        // 以回车为一行结束的标志按行读取文件,返回值是string类型
        line, err := buf.ReadString('\n') // 注意这里传入的分隔符是[]byte字节类型格式,必须使用单引号
        //if err != nil {                   // 遇到错误
        //  fmt.Println(err)
        //  fmt.Println("文件读取错误")
        //  break
        if err == io.EOF { //读到最后一行遇到结尾,正常切割字符串
            //遇到io.EOF则表示文件最后一个字符读完,即最后一行内容的结尾。
            // io.EOF可以看做是对文件最后一行内容的处理步骤
            // 这一点比较特殊,要特别处理一下最后一行内容的收集操作,不然最后一行就被丢弃了
            LineSlice := strings.Split(strings.Trim(line, "\n"), " ")
            fmt.Printf("%v\n", LineSlice)
            break
        } else if err != nil { // 遇到其他错误
            fmt.Println(err)
            fmt.Println("文件读取错误")
            break
        } else { // 没遇到 结尾或错误,正常切割字符串
            // strings.Trim(line, "\n")  去除每行字符串中的换行符
            // strings.Split(line, " ") 以空格<" ">做为分割付,切割字符串,返回slice
            LineSlice := strings.Split(strings.Trim(line, "\n"), " ")
            fmt.Printf("%v\n", LineSlice)
        }
    }
}

执行结果,每一行都被存入一个切片之中

% go run main.go
[113 334 556]
[113 324 589]
[115 331 557]
[456 213 509]

按行读取文件方法二(bufio.NewScanner)

package main

import (
   "bufio"
   "fmt"
   "os"
   "strings"
)

func main() {
   f, err := os.Open("config.txt")

   defer func() {
      if err := f.Close(); err != nil {
         fmt.Println(err)
      }
   }()

   if err != nil {
      fmt.Println(err)
   }

   scanner := bufio.NewScanner(f)
   for scanner.Scan() {
      line := scanner.Text()
      stringList := strings.Split(line, " ")
      fmt.Println(stringList)
   }
}

运行结果是一样的,明显方法二的代码更为简洁

% go run main.go
[113 334 556]
[113 324 589]
[115 331 557]
[456 213 509]

按行写入文件

package main

import (
  "bufio"
  "fmt"
  "os"
)

func main() {
  //OpenFile操作操作符:
  //O_RDONLY:只读模式打开文件;
  //O_WRONLY:只写模式打开文件;
  //O_RDWR:读写模式打开文件;
  //O_APPEND:写操作时将数据附加到文件尾部(追加);
  //O_CREATE:如果不存在将创建一个新文件;
  //O_EXCL:和 O_CREATE 配合使用,文件必须不存在,否则返回一个错误;
  //O_SYNC:当进行一系列写操作时,每次都要等待上次的 I/O 操作完成再进行;
  //O_TRUNC:如果可能,在打开时清空文件。
  
  
  // 文件不存在就创建并打开写模式,文件存在就打开追加模式
  f, err := os.OpenFile("config.txt", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
  defer f.Close()
  if err != nil {
    fmt.Println("文件打开失败")
  }

  // 实例化NewWriter()方法
  writer := bufio.NewWriter(f)

  // 在原文件后追加一个回车符
  if _, err := writer.WriteString("\n"); err != nil {
    fmt.Println("写入文件缓存失败")
  }

  // 写入5行字符串
  for i := 1; i < 6; i++ {
    str := fmt.Sprintf("写入第< %d >行内容\n", i)
    fmt.Printf("%s", str)

    // 把内容写入缓存
    // 返回写入的写入文件长度和写入结果
    if _, err := writer.WriteString(str); err != nil {
      fmt.Println("写入文件缓存失败")
      break
    }

    // 把缓存刷入磁盘
    if err := writer.Flush(); err != nil {
      fmt.Println("缓存内容写入磁盘失败")
      break
    }

  }

}

查看写入文件的效果

% cat config.txt 
113 334 556
113 324 589
115 331 557
456 213 509

写入第< 1 >行内容
写入第< 2 >行内容
写入第< 3 >行内容
写入第< 4 >行内容
写入第< 5 >行内容