Go語(yǔ)言的IO庫(kù)那么多糾結(jié)該如何選擇
在計(jì)算機(jī)和信息技術(shù)領(lǐng)域里 I/O 這個(gè)術(shù)語(yǔ)表示輸入 / 輸出 ( 英語(yǔ):Input / Output ) ,通常指數(shù)據(jù)在存儲(chǔ)器(內(nèi)部和外部)或其他周邊設(shè)備之間的輸入和輸出,是信息處理系統(tǒng)與外部之間的通信。輸入是系統(tǒng)接收的信號(hào)或數(shù)據(jù),輸出則是從其發(fā)送的信號(hào)或數(shù)據(jù)。
在Go語(yǔ)言中涉及 I/O 操作的內(nèi)置庫(kù)有很多種,比如: io 庫(kù), os 庫(kù), ioutil 庫(kù), bufio 庫(kù), bytes 庫(kù), strings 庫(kù)等等。擁有這么多內(nèi)置庫(kù)是好事,但是具體到涉及 I/O 的場(chǎng)景我們應(yīng)該選擇哪個(gè)庫(kù)呢?
io.Reader/Writer
Go語(yǔ)言里使用 io.Reader 和 io.Writer 兩個(gè) interface 來(lái)抽象 I/O ,他們的定義如下。
type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) }
io.Reader 接口代表一個(gè)可以從中讀取字節(jié)流的實(shí)體,而 io.Writer 則代表一個(gè)可以向其寫(xiě)入字節(jié)流的實(shí)體。
- io.Reader/Writer 常用的幾種實(shí)現(xiàn)
- net.Conn: 表示網(wǎng)絡(luò)連接。
- os.Stdin, os.Stdout, os.Stderr: 標(biāo)準(zhǔn)輸入、輸出和錯(cuò)誤。
- os.File: 網(wǎng)絡(luò),標(biāo)準(zhǔn)輸入輸出,文件的流讀取。
- strings.Reader: 字符串抽象成 io.Reader 的實(shí)現(xiàn)。
- bytes.Reader: []byte抽象成 io.Reader 的實(shí)現(xiàn)。
- bytes.Buffer: []byte抽象成 io.Reader 和 io.Writer 的實(shí)現(xiàn)。
- bufio.Reader/Writer: 抽帶緩沖的流讀取和寫(xiě)入(比如按行讀寫(xiě))。
除了這幾種實(shí)現(xiàn)外常用的還有 ioutil 工具庫(kù)包含了很多IO工具函數(shù),編碼相關(guān)的內(nèi)置庫(kù) encoding/base64 、 encoding/binary 等也是通過(guò) io.Reader 和 io.Writer 實(shí)現(xiàn)各自的編碼功能的。
這些常用實(shí)現(xiàn)和工具庫(kù)與io.Reader和io.Writer間的關(guān)系可以用下圖表示。
每種I/O庫(kù)的使用場(chǎng)景
io庫(kù)
io 庫(kù)屬于底層接口定義庫(kù)。它的作用主要是定義個(gè) I/O 的基本接口和個(gè)基本常量,并解釋這些接口的功能。在實(shí)際編寫(xiě)代碼做 I/O 操作時(shí),這個(gè)庫(kù)一般只用來(lái)調(diào)用它的常量和接口定義,比如用 io.EOF 判斷是否已經(jīng)讀取完,用 io.Reader 做變量的類(lèi)型聲明。
// 字節(jié)流讀取完后,會(huì)返回io.EOF這個(gè)error for { n, err := r.Read(buf) fmt.Println(n, err, buf[:n]) if err == io.EOF { break } }
os 庫(kù)
os 庫(kù)主要是處理操作系統(tǒng)操作的,它作為Go程序和操作系統(tǒng)交互的橋梁。創(chuàng)建文件、打開(kāi)或者關(guān)閉文件、Socket等等這些操作和都是和操作系統(tǒng)掛鉤的,所以都通過(guò) os 庫(kù)來(lái)執(zhí)行。這個(gè)庫(kù)經(jīng)常和 ioutil , bufio 等配合使用
ioutil庫(kù)
ioutil 庫(kù)是一個(gè)有工具包,它提供了很多使用的 IO 工具函數(shù),例如 ReadAll、ReadFile、WriteFile、ReadDir。唯一需要注意的是它們都是一次性讀取和一次性寫(xiě)入,所以使用時(shí),尤其是把數(shù)據(jù)從文件里一次性讀到內(nèi)存中時(shí)需要注意文件的大小。
讀出文件中的所有內(nèi)容
func readByFile() { data, err := ioutil.ReadFile( "./file/test.txt") if err != nil { log.Fatal("err:", err) return } fmt.Println("data", string(data)) }
將數(shù)據(jù)一次性寫(xiě)入文件
func writeFile() { err := ioutil.WriteFile("./file/write_test.txt", []byte("hello world!"), 0644) if err != nil { panic(err) return } }
bufio庫(kù)
bufio,可以理解為在 io 庫(kù)的基礎(chǔ)上額外封裝加了一個(gè)緩存層,它提供了很多按行進(jìn)行讀寫(xiě)的函數(shù),從io庫(kù)的按字節(jié)讀寫(xiě)變?yōu)榘葱凶x寫(xiě)對(duì)寫(xiě)代碼來(lái)說(shuō)還是方便了不少。
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 // 循環(huán)中打印前100行內(nèi)容 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")都是按行讀,只不過(guò)ReadLine讀出來(lái)的是[]byte,后者直接讀出了string,最終他們底層調(diào)用的都是ReadSlice方法。
- bufio VS ioutil 庫(kù):bufio VS 和 ioutil 庫(kù)都提供了讀寫(xiě)文件的能力。它們之間唯一的區(qū)別是 bufio 有一個(gè)額外的緩存層。這個(gè)優(yōu)勢(shì)主要體現(xiàn)在讀取大文件的時(shí)候。
bytes 和 strings 庫(kù)
bytes 和 strings 庫(kù)里的 bytes.Reader 和string.Reader,它們都實(shí)現(xiàn)了 io.Reader 接口,也都提供了NewReader方法用來(lái)從 []byte 或者 string 類(lèi)型的變量直接構(gòu)建出相應(yīng)的Reader實(shí)現(xiàn)。
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 } }
另一個(gè)區(qū)別是 bytes 庫(kù)有Buffer的功能,而 strings 庫(kù)則沒(méi)有。
var buf bytes.Buffer fmt.Fprintf(&buf, "Size: %d MB.", 85) s := buf.String()) // s == "Size: 85 MB."
總結(jié)
關(guān)于 io.Reader 和 io.Writer 接口,可以簡(jiǎn)單理解為讀源和寫(xiě)源。也就是說(shuō),只要實(shí)現(xiàn)了 Reader 中的 Read 方法,這個(gè)東西就可以作為讀源,里面可以包含數(shù)據(jù),被我們讀取。 Writer 也是如此。
以上是我對(duì)Go語(yǔ)言里做 I/O 操作時(shí)經(jīng)常會(huì)用到的Go語(yǔ)言?xún)?nèi)置庫(kù)在使用場(chǎng)景和每個(gè)庫(kù)要解決的問(wèn)題上的一些總結(jié),希望能幫大家理清思路,作為參考,在開(kāi)發(fā)任務(wù)中需要時(shí)正確選擇合適的庫(kù)完成 I/O 操作。如果文章中的敘述有錯(cuò)誤,歡迎留言指正,也歡迎在留言中對(duì)文章內(nèi)容進(jìn)行探討和提出建議。
以上就是Go語(yǔ)言的IO庫(kù)那么多糾結(jié)該如何選擇的詳細(xì)內(nèi)容,更多關(guān)于Go語(yǔ)言IO庫(kù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
幾個(gè)小技巧幫你實(shí)現(xiàn)Golang永久阻塞
Go 的運(yùn)行時(shí)的當(dāng)前設(shè)計(jì),假定程序員自己負(fù)責(zé)檢測(cè)何時(shí)終止一個(gè) goroutine 以及何時(shí)終止該程序。有時(shí)候我們需要的是使程序阻塞在這一行,本文就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下2021-12-12golang實(shí)現(xiàn)ip訪問(wèn)限制及提交次數(shù)
在?Web?應(yīng)用中,通常會(huì)需要對(duì)?IP?訪問(wèn)進(jìn)行限制以及控制提交次數(shù),本文將使用中間件或者基于?Redis?這樣的緩存服務(wù)來(lái)實(shí)現(xiàn),感興趣的可以了解下2024-10-10golang多維度排序及題解最長(zhǎng)連續(xù)序列
這篇文章主要為大家介紹了golang多維度排序及題解最長(zhǎng)連續(xù)序列示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Golang 實(shí)現(xiàn) Redis系列(六)如何實(shí)現(xiàn) pipeline 模式的 redis 客戶(hù)端
pipeline 模式的 redis 客戶(hù)端需要有兩個(gè)后臺(tái)協(xié)程負(fù)責(zé) tcp 通信,調(diào)用方通過(guò) channel 向后臺(tái)協(xié)程發(fā)送指令,并阻塞等待直到收到響應(yīng),本文是使用 golang 實(shí)現(xiàn) redis 系列的第六篇, 將介紹如何實(shí)現(xiàn)一個(gè) Pipeline 模式的 Redis 客戶(hù)端。2021-07-07Go語(yǔ)言sync包與鎖實(shí)現(xiàn)限制線程對(duì)變量的訪問(wèn)
本文主要介紹了Go語(yǔ)言sync包與鎖實(shí)現(xiàn)限制線程對(duì)變量的訪問(wèn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04在 Golang 中實(shí)現(xiàn) Cache::remember 方法詳解
這篇文章主要介紹了在 Golang 中實(shí)現(xiàn) Cache::remember 方法詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03