談?wù)剬olang IO讀寫的困惑
前言
Golang的IO讀寫提供了很多種方式,目前本人知道的有io庫、os庫、ioutil庫、bufio庫、bytes/strings庫等。
雖然庫多是一件好事,意味著選擇性多,但讓我困惑的一點(diǎn)是:什么場景下該用哪個庫? 為什么?
在給出結(jié)論前,我先想給出Golang內(nèi)置IO庫的項(xiàng)目結(jié)構(gòu),主要方便理解和引用:
# 只列舉了核心的目錄及文件 src: - bufio - bufio.go - bytes - buffer.go - reader.go - io - ioutil - ioutil.go - io.go - os - file.go - strings - reader.go
1.io庫屬于底層接口定義庫,其作用是是定義一些基本接口和一些基本常量,并對這些接口的作用給出說明,常見的接口有Reader、Writer等。一般用這個庫只是為了調(diào)用它的一些常量,比如io.EOF。
2.ioutil庫包含在io目錄下,它的主要作用是作為一個工具包,里面有一些比較實(shí)用的函數(shù),比如 ReadAll(從某個源讀取數(shù)據(jù))、ReadFile(讀取文件內(nèi)容)、WriteFile(將數(shù)據(jù)寫入文件)、ReadDir(獲取目錄)
3.os庫主要是跟操作系統(tǒng)打交道,所以文件操作基本都會跟os庫掛鉤,比如創(chuàng)建文件、打開一個文件等。這個庫往往會和ioutil庫、bufio庫等配合使用
4.bufio庫可以理解為在io庫上再封裝一層,加上了緩存功能。它可能會和ioutil庫和bytes.Buffer搞混。
4.1 bufio VS ioutil庫:兩者都提供了對文件的讀寫功能,唯一的不同就是bufio多了一層緩存的功能,這個優(yōu)勢主要體現(xiàn)讀取大文件的時候(ioutil.ReadFile是一次性將內(nèi)容加載到內(nèi)存,如果內(nèi)容過大,很容易爆內(nèi)存)
4.2 bufio VS bytes.Buffer:兩者都提供一層緩存功能,它們的不同主要在于 bufio 針對的是文件到內(nèi)存的緩存,而 bytes.Buffer 的針對的是內(nèi)存到內(nèi)存的緩存(個人感覺有點(diǎn)像channel,你也可以發(fā)現(xiàn) bytes.Buffer 并沒有提供接口將數(shù)據(jù)寫到文件)。
5.bytes和strings庫:這兩個庫有點(diǎn)迷,首先它們都實(shí)現(xiàn)了Reader接口,所以它們的不同主要在于針對的對象不同,bytes針對的是字節(jié),strings針對的是字符串(它們的方法實(shí)現(xiàn)原理很相似)。另一個區(qū)別就是 bytes還帶有Buffer的功能,但是 strings沒提供。
注:關(guān)于Reader和Writer接口,可以簡單理解為讀取源和寫入源,即只要實(shí)現(xiàn)Reader里面的Read方法,這個東西就可以作為一個讀取源,里面可以包含數(shù)據(jù)并被我們讀??;Writer亦是如此。
以上就是個人的一些結(jié)論,下面會針對以上結(jié)論做進(jìn)一步說明,如果有錯誤的地方麻煩請留言指正,比心❤️!
窺探 io 庫
io庫比較常用的接口有三個,分別是Reader,Writer和Close。
// Read方法會接收一個字節(jié)數(shù)組p,并將讀取到的數(shù)據(jù)存進(jìn)該數(shù)組,最后返回讀取的字節(jié)數(shù)n。 // 注意n不一定等于讀取的數(shù)據(jù)長度,比如字節(jié)數(shù)組p的容量太小,n會等于數(shù)組的長度 type Reader interface { Read(p []byte) (n int, err error) } // Write 方法同樣接收一個字節(jié)數(shù)組p,并將接收的數(shù)據(jù)保存至文件或者標(biāo)準(zhǔn)輸出等,返回的n表示寫入的數(shù)據(jù)長度。 // 當(dāng)n不等于len(p)時,返回一個錯誤。 type Writer interface { Write(p []byte) (n int, err error) } // 關(guān)閉操作 type Closer interface { Close() error }
關(guān)于 Read 方法的具體實(shí)現(xiàn),可以在strings庫中看到:
// 定義一個Reader接口體 type Reader struct { s string i int64 // current reading index prevRune int // index of previous rune; or < 0 } // 通過NewReader方法得到 reader 對象,這里有個關(guān)鍵的地方是傳入的字符串被賦值到 s 變量中 func NewReader(s string) *Reader { return &Reader{s, 0, -1} } // Read方法: 核心是 copy 方法,參數(shù)b雖然是切片,但是copy方法會影響到它的底層數(shù)組 func (r *Reader) Read(b []byte) (n int, err error) { if r.i >= int64(len(r.s)) { return 0, io.EOF } r.prevRune = -1 // 核心方法 n = copy(b, r.s[r.i:]) r.i += int64(n) return }
窺探 ioutil 庫
上面提到,ioutil 庫就是一個工具包,里面主要是比較實(shí)用的函數(shù),比如ReadFile、WriteFile等,唯一需要注意的是它們都是一次性讀取和一次性寫入,所以當(dāng)讀取的時候注意文件不能過大。
從文件讀取數(shù)據(jù):
func readByFile() { data, err := ioutil.ReadFile( "./lab8_io/file/test.txt") if err != nil { log.Fatal("err:", err) return } fmt.Println("data", string(data)) // hello world! }
把數(shù)據(jù)寫入到文件:
func writeFile() { err := ioutil.WriteFile("./lab8_io/file/write_test.txt", []byte("hello world!"), 0644) if err != nil { panic(err) return } }
遍歷目錄:遍歷目錄有一個需要注意的是它的排序并不是自然排序方式。
窺探bufio庫
bufio 庫在上面也提到過,它主要是在io庫上加了一層緩存的功能,以下是bufio讀取大文件的例子:
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 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 }
注:
1.bufio 的ReadLine/ReadBytes/ReadString/ReadSlice: ReadString和ReadBytes等同,ReadBytes和ReadLine都調(diào)用了ReadSlice
窺探bytes/strings庫
前面提過,就單純實(shí)現(xiàn)Reader接口,bytes和strings底層函數(shù)的實(shí)現(xiàn)方式是差不多的,可以查看其源碼得證:
// bytes/reader.go // Read implements the io.Reader interface. func (r *Reader) Read(b []byte) (n int, err error) { if r.i >= int64(len(r.s)) { return 0, io.EOF } r.prevRune = -1 n = copy(b, r.s[r.i:]) r.i += int64(n) return } // strings/reader.go func (r *Reader) Read(b []byte) (n int, err error) { if r.i >= int64(len(r.s)) { return 0, io.EOF } r.prevRune = -1 n = copy(b, r.s[r.i:]) r.i += int64(n) return }
參考/推薦
詳解golang中bufio包的實(shí)現(xiàn)原理
Golang 超大文件讀取的兩個方案
https://gist.github.com/suntong/032173e96247c0411140
到此這篇關(guān)于談?wù)剬olang IO讀寫的困惑的文章就介紹到這了,更多相關(guān)Golang IO讀寫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang在GRPC中設(shè)置client的超時時間
這篇文章主要介紹了golang在GRPC中設(shè)置client的超時時間,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04golang語言中for循環(huán)語句用法實(shí)例
這篇文章主要介紹了golang語言中for循環(huán)語句用法,實(shí)例分析了for循環(huán)遍歷的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-01-01golang雪花算法實(shí)現(xiàn)64位的ID的示例代碼
本文展示了使用Go語言實(shí)現(xiàn)雪花算法生成64位ID的示例代碼,雪花算法通過當(dāng)前時間戳、工作節(jié)點(diǎn)ID、數(shù)據(jù)中心ID和序列號生成唯一的64位ID,確保在分布式系統(tǒng)中的唯一性和時間順序性,感興趣的可以了解一下2024-09-09Go實(shí)現(xiàn)字符串與數(shù)字的高效轉(zhuǎn)換
在軟件開發(fā)的世界里,數(shù)據(jù)類型轉(zhuǎn)換是一項(xiàng)基礎(chǔ)而重要的技能,尤其在Go語言這樣類型嚴(yán)格的語言中,正確高效地進(jìn)行類型轉(zhuǎn)換對于性能優(yōu)化和代碼質(zhì)量至關(guān)重要,本文給大家介紹了Go實(shí)現(xiàn)字符串與數(shù)字的高效轉(zhuǎn)換,需要的朋友可以參考下2024-02-02golang打包成帶圖標(biāo)的exe可執(zhí)行文件
這篇文章主要給大家介紹了關(guān)于golang打包成帶圖標(biāo)的exe可執(zhí)行文件的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2023-06-06