Golang的IO庫那麼多,我該怎麼選?

在計算機和信息技術領域裏I/O這個術語表示輸入 / 輸出 ( 英語:Input / Output ) ,一般指數據在存儲器(內部和外部)或其餘周邊設備之間的輸入和輸出,是信息處理系統與外部之間的通訊。輸入是系統接收的信號或數據,輸出則是從其發送的信號或數據。緩存

在Go語言中涉及I/O操做的內置庫有不少種,好比:io庫,os庫,ioutil庫,bufio庫,bytes庫,strings庫等等。 擁有這麼多內置庫是好事,可是具體到涉及I/O的場景咱們應該選擇哪一個庫呢?微信

io.Reader/Writer

Go語言裏使用io.Readerio.Writer兩個 interface 來抽象I/O,他們的定義以下。markdown

type Reader interface {
	Read(p []byte) (n int, err error)
}

type Writer interface {
	Write(p []byte) (n int, err error)
}
複製代碼

io.Reader 接口表明一個能夠從中讀取字節流的實體,而io.Writer則掉吧一個能夠向其寫入字節流的實體。網絡

io.Reader/Writer 經常使用的幾種實現

  • net.Conn: 標識網絡鏈接。
  • os.Stdin, os.Stdout, os.Stderr: 標準輸入、輸出和錯誤。
  • os.File: 網絡,標準輸入輸出,文件的流讀取。
  • strings.Reader: 字符串抽象成 io.Reader 的實現。
  • bytes.Reader: []byte抽象成 io.Reader 的實現。
  • bytes.Buffer: []byte抽象成 io.Reader 和 io.Writer 的實現。
  • bufio.Reader/Writer: 帶緩衝的流讀取和寫入(好比按行讀寫)。

除了這幾種實現外經常使用的還有ioutil工具庫包含了不少IO工具函數,編碼相關的內置庫encoding/base64encoding/binary等也是經過 io.Reader 和 io.Writer 實現各自的編碼功能的。函數

這些經常使用實現和工具庫與io.Reader和io.Writer間的關係能夠用下圖表示。工具

每種I/O庫的使用場景

io庫

io庫屬於底層接口定義庫。它的做用主要是定義個I/O的基本接口和個基本常量,並解釋這些接口的功能。在實際編寫代碼作I/O操做時,這個庫通常只用來調用它的常量和接口定義,好比用io.EOF判斷是否已經讀取完,用io.Reader作變量的類型聲明。ui

// 字節流讀取完後,會返回io.EOF這個error
for {
	n, err := r.Read(buf)
	fmt.Println(n, err, buf[:n])
	if err == io.EOF {
		break
	}
}
複製代碼

os 庫

os庫主要是處理操做系統操做的,它做爲Go程序和操做系統交互的橋樑。建立文件、打開或者關閉文件、Socket等等這些操做和都是和操做系統掛鉤的,因此都經過os庫來執行。這個庫常常和ioutilbufio等配合使用編碼

ioutil庫

ioutil庫是一個有工具包,它提供了不少實用的 IO 工具函數,例如 ReadAll、ReadFile、WriteFile、ReadDir。惟一須要注意的是它們都是一次性讀取和一次性寫入,因此使用時,尤爲是把數據從文件裏一次性讀到內存中時須要注意文件的大小。spa

讀出文件中的全部內容操作系統

func readByFile() {
  data, err := ioutil.ReadFile( "./file/test.txt")
  if err != nil {
    log.Fatal("err:", err)
    return
  }
  fmt.Println("data", string(data)) 
}
複製代碼

將數據一次性寫入文件

func writeFile() {
  err := ioutil.WriteFile("./file/write_test.txt", []byte("hello world!"), 0644)
  if err != nil {
    panic(err)
    return
  }
}
複製代碼

bufio庫

bufio,能夠理解爲在io庫的基礎上額外封裝加了一個緩存層,它提供了不少按行進行讀寫的函數,從io庫的按字節讀寫變爲按行讀寫對寫代碼來講仍是方便了很多。

func readBigFile(filePath string) error {
  f, err := os.Open(filePath)
  defer f.Close()

  if err != nil {
    log.Fatal(err)
    return err
  }

  buf := bufio.NewReader(f)
  count := 0
  // 循環中打印前100行內容
  for {
    count += 1
    line, err := buf.ReadString('\n')
    line = strings.TrimSpace(line)
    if err != nil {
      return err
    }
    fmt.Println("line", line)

    if count > 100 {
      break
    }
  }
  return nil
}
複製代碼
  • ReadLine和ReadString方法:buf.ReadLine(),buf.ReadString("\n")都是按行讀,只不過ReadLine讀出來的是[]byte,後者直接讀出了string,最終他們底層調用的都是ReadSlice方法。
  • bufio VS ioutil 庫: bufio 和 ioutil 庫都提供了讀寫文件的能力。它們之間惟一的區別是 bufio 有一個額外的緩存層。這個優點主要體如今讀取大文件的時候。

bytes 和 strings 庫

bytes 和 strings 庫裏的 bytes.Reader 和string.Reader,它們都實現了io.Reader接口,也都提供了NewReader方法用來從[]byte或者string類型的變量直接構建出相應的Reader實現。

r := strings.NewReader("abcde")
// 或者是 bytes.NewReader([]byte("abcde"))
buf := make([]byte, 4)
for {
	n, err := r.Read(buf)
	fmt.Println(n, err, buf[:n])
	if err == io.EOF {
		break
	}
}
複製代碼

另外一個區別是bytes庫有Buffer的功能,而strings庫則沒有。

var buf bytes.Buffer
fmt.Fprintf(&buf, "Size: %d MB.", 85)
s := buf.String()) // s == "Size: 85 MB."
複製代碼

總結

關於io.Readerio.Writer接口,能夠簡單理解爲讀源和寫源。也就是說,只要實現了Reader中的Read方法,這個東西就能夠做爲讀源,裏面能夠包含數據,被咱們讀取。Writer也是如此。

以上是我對Go語言裏作I/O操做時常常會用到的Go語言內置庫在使用場景和每一個庫要解決的問題上的一些總結,但願能幫你們理清思路,做爲參考,在開發任務中須要時正確選擇合適的庫完成I/O操做。若是文章中的敘述有錯誤,歡迎留言指正,也歡迎在留言中對文章內容進行探討和提出建議。

今天的文章就到這裏啦,若是喜歡個人文章就幫我點個贊吧,我會每週經過技術文章分享個人所學所見和第一手實踐經驗,感謝你的支持。微信搜索關注公衆號「網管叨bi叨」每週教會你一個進階知識。

相關文章
相關標籤/搜索