Golang 讀、寫檔案

Jerry發表於2019-02-16

檔案的讀寫是程式語言的常見操作之一,這裡講一些Goang 讀取檔案的相關操作。

讀取檔案

讀取檔案有三種方式:

  • 將檔案整個讀入記憶體
  • 按位元組數讀取
  • 按行讀取

具體實現如下:

1、將檔案整個讀入記憶體
package main

import (
   "os"
   "io/ioutil"
   "fmt"
)

func main() {
   file, err := os.Open("D:/gopath/src/golang_development_notes/example/log.txt")
   if err != nil {
      panic(err)
   }
   defer file.Close()
   content, err := ioutil.ReadAll(file)
   fmt.Println(string(content))
}

或者

package main

import (
   "os"
   "io/ioutil"
   "fmt"
)

func main() {
   filepath := "D:/gopath/src/golang_development_notes/example/log.txt"
   content ,err :=ioutil.ReadFile(filepath)
   if err !=nil {
      panic(err)
   }
   fmt.Println(string(content))
}

將檔案整個讀入記憶體,效率比較高,佔用記憶體也最高。

2、按位元組讀取檔案

package main

import (
   "bufio"
   "fmt"
   "io"
   "io/ioutil"
   "os"
)

func main() {
   filepath := "D:/gopath/src/golang_development_notes/example/log.txt"
   fi, err := os.Open(filepath)
   if err != nil {
      panic(err)
   }
   defer fi.Close()
   r := bufio.NewReader(fi)

   chunks := make([]byte, 0)
   buf := make([]byte, 1024) //一次讀取多少個位元組
   for {
      n, err := r.Read(buf)
      if err != nil && err != io.EOF {
         panic(err)
      }
      fmt.Println(string(buf[:n]))
      break
      if 0 == n {
         break
      }
      chunks = append(chunks, buf[:n]...)
   }
   fmt.Println(string(chunks))
}

package main

import (
   "fmt"
   "io"
   "os"
)

func main() {

   file := "D:/gopath/src/golang_development_notes/example/log.txt"
   f, err := os.Open(file)
   if err != nil {
      panic(err)
   }
   defer f.Close()

   chunks := make([]byte, 0)
   buf := make([]byte, 1024)
   for {
      n, err := f.Read(buf)
      if err != nil && err != io.EOF {
         panic(err)
      }
      if 0 == n {
         break
      }
      chunks = append(chunks, buf[:n]...)
   }
   fmt.Println(string(chunks))
}

3、按行讀取

package main

import (
   "bufio"
   "fmt"
   "io"
   "io/ioutil"
   "os"
   "strings"
)

func main() {
   filepath := "D:/gopath/src/golang_development_notes/example/log.txt"
   file, err := os.OpenFile(filepath, os.O_RDWR, 0666)
   if err != nil {
      fmt.Println("Open file error!", err)
      return
   }
   defer file.Close()

   stat, err := file.Stat()
   if err != nil {
      panic(err)
   }
   var size = stat.Size()
   fmt.Println("file size=", size)

   buf := bufio.NewReader(file)
   for {
      line, err := buf.ReadString(`
`)
      line = strings.TrimSpace(line)
      fmt.Println(line)
      if err != nil {
         if err == io.EOF {
            fmt.Println("File read ok!")
            break
         } else {
            fmt.Println("Read file error!", err)
            return
         }
      }
   }

}

寫入檔案

有以下寫入方式

1、ioutil.WriteFile

package main

import (
   "io/ioutil"
)

func main() {

   content := []byte("測試1
測試2
")
   err := ioutil.WriteFile("test.txt", content, 0644)
   if err != nil {
      panic(err)
   }
}

這種方式每次都會覆蓋 test.txt內容,如果test.txt檔案不存在會建立。

2、os

package main

import (
   "fmt"
   "io"
   "os"
)

func checkFileIsExist(filename string) bool {
   if _, err := os.Stat(filename); os.IsNotExist(err) {
      return false
   }
   return true
}
func main() {
   var wireteString = "測試1
測試2
"
   var filename = "./test.txt"
   var f *os.File
   var err1 error
   if checkFileIsExist(filename) { //如果檔案存在
      f, err1 = os.OpenFile(filename, os.O_APPEND, 0666) //開啟檔案
      fmt.Println("檔案存在")
   } else {
      f, err1 = os.Create(filename) //建立檔案
      fmt.Println("檔案不存在")
   }
   defer f.Close()
   n, err1 := io.WriteString(f, wireteString) //寫入檔案(字串)
   if err1 != nil {
      panic(err1)
   }
   fmt.Printf("寫入 %d 個位元組n", n)
}

此種方法可以在檔案內容末尾新增新內容。

3、

package main

import (
   "fmt"
   "os"
)

func checkFileIsExist(filename string) bool {
   if _, err := os.Stat(filename); os.IsNotExist(err) {
      return false
   }
   return true
}
func main() {
   var str = "測試1
測試2
"
   var filename = "./test.txt"
   var f *os.File
   var err1 error
   if checkFileIsExist(filename) { //如果檔案存在
      f, err1 = os.OpenFile(filename, os.O_APPEND, 0666) //開啟檔案
      fmt.Println("檔案存在")
   } else {
      f, err1 = os.Create(filename) //建立檔案
      fmt.Println("檔案不存在")
   }
   defer f.Close()
   n, err1 := f.Write([]byte(str)) //寫入檔案(位元組陣列)

   fmt.Printf("寫入 %d 個位元組n", n)
   n, err1 = f.WriteString(str) //寫入檔案(字串)
   if err1 != nil {
      panic(err1)
   }
   fmt.Printf("寫入 %d 個位元組n", n)
   f.Sync()
}

此種方法可以在檔案內容末尾新增新內容。

4、bufio

package main

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

func checkFileIsExist(filename string) bool {
   if _, err := os.Stat(filename); os.IsNotExist(err) {
      return false
   }
   return true
}
func main() {
   var str = "測試1
測試2
"
   var filename = "./test.txt"
   var f *os.File
   var err1 error
   if checkFileIsExist(filename) { //如果檔案存在
      f, err1 = os.OpenFile(filename, os.O_APPEND, 0666) //開啟檔案
      fmt.Println("檔案存在")
   } else {
      f, err1 = os.Create(filename) //建立檔案
      fmt.Println("檔案不存在")
   }
   defer f.Close()
   if err1 != nil {
      panic(err1)
   }
   w := bufio.NewWriter(f) //建立新的 Writer 物件
   n, _ := w.WriteString(str)
   fmt.Printf("寫入 %d 個位元組n", n)
   w.Flush()
}

此種方法可以在檔案內容末尾新增新內容。

對比幾種讀取檔案效率

package main

import (
   "bufio"
   "fmt"
   "io"
   "io/ioutil"
   "os"
   "time"
)

func read0(path string) string {
   file, err := os.Open(path)
   if err != nil {
      panic(err)
   }
   defer file.Close()
   content, err := ioutil.ReadAll(file)
   return string(content)
}

func read1(path string) string {
   content, err := ioutil.ReadFile(path)
   if err != nil {
      panic(err)
   }
   return string(content)
}

func read2(path string) string {
   fi, err := os.Open(path)
   if err != nil {
      panic(err)
   }
   defer fi.Close()
   r := bufio.NewReader(fi)

   chunks := make([]byte, 0)
   buf := make([]byte, 1024) //一次讀取多少個位元組
   for {
      n, err := r.Read(buf)
      if err != nil && err != io.EOF {
         panic(err)
      }
      if 0 == n {
         break
      }
      chunks = append(chunks, buf[:n]...)
   }
   return string(chunks)
}

func read3(path string) string {
   fi, err := os.Open(path)
   if err != nil {
      panic(err)
   }
   defer fi.Close()

   chunks := make([]byte, 0)
   buf := make([]byte, 1024)
   for {
      n, err := fi.Read(buf)
      if err != nil && err != io.EOF {
         panic(err)
      }
      if 0 == n {
         break
      }
      chunks = append(chunks, buf[:n]...)
   }
   return string(chunks)
}

func main() {

   file := "D:/gopath/src/example/example/log.txt"

   start := time.Now()

   read0(file)
   t0 := time.Now()
   fmt.Printf("Cost time %v
", t0.Sub(start))

   read1(file)
   t1 := time.Now()
   fmt.Printf("Cost time %v
", t1.Sub(t0))

   read2(file)
   t2 := time.Now()
   fmt.Printf("Cost time %v
", t2.Sub(t1))

   read3(file)
   t3 := time.Now()
   fmt.Printf("Cost time %v
", t3.Sub(t2))

}

執行結果:

第一次

Cost time 6.0003ms
Cost time 3.0002ms
Cost time 7.0004ms
Cost time 11.0006ms

第二次

Cost time 7.0004ms
Cost time 4.0003ms
Cost time 6.0003ms
Cost time 8.0005ms

第三次

Cost time 9.0006ms
Cost time 3.0001ms
Cost time 7.0004ms
Cost time 11.0007ms

links