Cho-Ching's Blog

[Go] by Example 筆記(八) -- Reading files

讀取整個檔案

最基本讀取檔案的方式, 就是讀取整個檔案到記憶體中去。

Go提供了 ioutil package, 實現了一些實用的I/O函式,以下 利用ioutil.ReadFile:

package main
import (
  "fmt"
  "io/ioutil"
)
func check(e error) {
  if e != nil {
    panic(e)
  }
}
func main() {
  dat, err := ioutil.ReadFile("./test")
  check(err)
  fmt.Print(string(dat))
}

ioutil.ReadFile定義:

func ReadFile(filename string) ([]byte, error)

注意引用ioutil是io/ioutil而不是ioutil

將檔案讀取成byte型式的slice。

檔案不大的時候, ioutil.ReadFile提供了很方便的選擇, 注意ReadFile成功時候err == nil而不是err == EOF

打開檔案

如果想要對讀取檔案做更多的控制, 那就需要利用Go的 os package, os package提供跟平台有關的操作介面例如檔案操作。 這裡利用os.Open函式:

import (
  "os"
  "log"
)
file, err:=os.Open("file.go") //for read access.
defer file.Close()
if err != nil {
  log.Fatal(err)
}

注意打開檔案後做完所有處理後, 我們利用defer file.Close()將開啟的檔案關閉。

打開檔案失敗的話, 錯誤訊息就會自我解釋:

open file.go: no such file or directory

os.Open傳回一個*File型別, File實作io.Reader interface:

func Open(name string) (file *File, err error)

os.Open或是os.Create都是呼叫os.OpenFile:

func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

flag例如是O_RDONLY只允許讀取, perm像是0666這樣。os.Open就是呼叫os.OpenFile, 傳入的flag參數為O_RDONLY(只允許讀取)。

type File

os.File可以執行的動作如下:

os.File

例如把檔案的資料讀到一個bytes slice, file.Read(data)傳回讀取到的bytes計數:

data := make([]byte, 100)
count, err := file.Read(data)

if err != nil {
  log.Fatal(err)
}
fmt.Printf("read %d bytes: %q\n", count, data[:count])

一行一行讀取

Go bufio package實作 buffered I/O, 包裹io.Reader或是io.Writer變成新的Reader以及Writer, 新的Reader和Writer就提供了buffer的功能, 特別用在文字處理上。

這裡我們使用ReadBytes('\n')或是ReadString('\n')或是用Scanner(簡單使用還是推荐用Scanner)來達成一行一行讀取檔案的目的。

func NewScanner(r io.Reader) *Scanner

我們利用bufio.NewScnner取得檔案或是STDIN的*Scanner

Scanner型別提供一個用來讀取資料方便的介面。像是一個用newline符號來分行的文字檔案。#Scanner

Scanner型別可以使用的function包括:

bufio.Scanner

實作一行一行讀取檔案如下:

package main
import (
  "bufio"
  "fmt"
  "os"
)
func main() {
  f, err := os.Open("./test")
  check(err)
  defer f.Close()
  scanner := bufio.NewScanner(f)
  for scanner.Scan() {
    fmt.Println(scanner.Text())
  }
}

利用scanner.Scan()判定是否還有下一行, 再利用scanner.Text()或是 scanner.Bytes[]讀取每行資料。

使用 ioutil

當然,另外一種作法就是使用ioutil.ReadFile讀取整個檔案, 再利用strings.Split切割:

package main
import (
  "fmt"
  "io/ioutil"
  "strings"
)
func main() {
  doc, err := ioutil.ReadFile("./test.md")
  if err != nil {
    panic(err)
  }
  lines := strings.Split(string(doc), "\n")
  for _, line := range lines {
    fmt.Printf("%q\n", line)
  } 
}

問題是這樣最後會多出一行...? .

More

go by example - reading files

golang - read text file into string array and write

<< 回到文章列表