在計(jì)算機(jī)和信息技術(shù)領(lǐng)域里 I/O 這個術(shù)語表示輸入 / 輸出 ( 英語:Input / Output ) ,通常指數(shù)據(jù)在存儲器(內(nèi)部和外部)或其他周邊設(shè)備之間的輸入和輸出,是信息處理系統(tǒng)與外部之間的通信。輸入是系統(tǒng)接收的信號或數(shù)據(jù),輸出則是從其發(fā)送的信號或數(shù)據(jù)。
在Go語言中涉及 I/O 操作的內(nèi)置庫有很多種,比如: io 庫, os 庫, ioutil 庫, bufio 庫, bytes 庫, strings 庫等等。擁有這么多內(nèi)置庫是好事,但是具體到涉及 I/O 的場景我們應(yīng)該選擇哪個庫呢?
io.Reader/Writer
Go語言里使用 io.Reader 和 io.Writer 兩個 interface 來抽象 I/O ,他們的定義如下。
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
io.Reader 接口代表一個可以從中讀取字節(jié)流的實(shí)體,而 io.Writer 則代表一個可以向其寫入字節(jié)流的實(shí)體。
- io.Reader/Writer 常用的幾種實(shí)現(xiàn)
- net.Conn: 表示網(wǎng)絡(luò)連接。
- os.Stdin, os.Stdout, os.Stderr: 標(biāo)準(zhǔn)輸入、輸出和錯誤。
- 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: 抽帶緩沖的流讀取和寫入(比如按行讀寫)。
除了這幾種實(shí)現(xiàn)外常用的還有 ioutil 工具庫包含了很多IO工具函數(shù),編碼相關(guān)的內(nèi)置庫 encoding/base64 、 encoding/binary 等也是通過 io.Reader 和 io.Writer 實(shí)現(xiàn)各自的編碼功能的。
這些常用實(shí)現(xiàn)和工具庫與io.Reader和io.Writer間的關(guān)系可以用下圖表示。
每種I/O庫的使用場景
io庫
io 庫屬于底層接口定義庫。它的作用主要是定義個 I/O 的基本接口和個基本常量,并解釋這些接口的功能。在實(shí)際編寫代碼做 I/O 操作時,這個庫一般只用來調(diào)用它的常量和接口定義,比如用 io.EOF 判斷是否已經(jīng)讀取完,用 io.Reader 做變量的類型聲明。
// 字節(jié)流讀取完后,會返回io.EOF這個error
for {
n, err := r.Read(buf)
fmt.Println(n, err, buf[:n])
if err == io.EOF {
break
}
}
os 庫
os 庫主要是處理操作系統(tǒng)操作的,它作為Go程序和操作系統(tǒng)交互的橋梁。創(chuàng)建文件、打開或者關(guān)閉文件、Socket等等這些操作和都是和操作系統(tǒng)掛鉤的,所以都通過 os 庫來執(zhí)行。這個庫經(jīng)常和 ioutil , bufio 等配合使用
ioutil庫
ioutil 庫是一個有工具包,它提供了很多使用的 IO 工具函數(shù),例如 ReadAll、ReadFile、WriteFile、ReadDir。唯一需要注意的是它們都是一次性讀取和一次性寫入,所以使用時,尤其是把數(shù)據(jù)從文件里一次性讀到內(nèi)存中時需要注意文件的大小。
讀出文件中的所有內(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ù)一次性寫入文件
func writeFile() {
err := ioutil.WriteFile("./file/write_test.txt", []byte("hello world!"), 0644)
if err != nil {
panic(err)
return
}
}
bufio庫
bufio,可以理解為在 io 庫的基礎(chǔ)上額外封裝加了一個緩存層,它提供了很多按行進(jìn)行讀寫的函數(shù),從io庫的按字節(jié)讀寫變?yōu)榘葱凶x寫對寫代碼來說還是方便了不少。
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")都是按行讀,只不過ReadLine讀出來的是[]byte,后者直接讀出了string,最終他們底層調(diào)用的都是ReadSlice方法。
- bufio VS ioutil 庫:bufio VS 和 ioutil 庫都提供了讀寫文件的能力。它們之間唯一的區(qū)別是 bufio 有一個額外的緩存層。這個優(yōu)勢主要體現(xiàn)在讀取大文件的時候。
bytes 和 strings 庫
bytes 和 strings 庫里的 bytes.Reader 和string.Reader,它們都實(shí)現(xiàn)了 io.Reader 接口,也都提供了NewReader方法用來從 []byte 或者 string 類型的變量直接構(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
}
}
另一個區(qū)別是 bytes 庫有Buffer的功能,而 strings 庫則沒有。
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 接口,可以簡單理解為讀源和寫源。也就是說,只要實(shí)現(xiàn)了 Reader 中的 Read 方法,這個東西就可以作為讀源,里面可以包含數(shù)據(jù),被我們讀取。 Writer 也是如此。
以上是我對Go語言里做 I/O 操作時經(jīng)常會用到的Go語言內(nèi)置庫在使用場景和每個庫要解決的問題上的一些總結(jié),希望能幫大家理清思路,作為參考,在開發(fā)任務(wù)中需要時正確選擇合適的庫完成 I/O 操作。如果文章中的敘述有錯誤,歡迎留言指正,也歡迎在留言中對文章內(nèi)容進(jìn)行探討和提出建議。
以上就是Go語言的IO庫那么多糾結(jié)該如何選擇的詳細(xì)內(nèi)容,更多關(guān)于Go語言IO庫的資料請關(guān)注腳本之家其它相關(guān)文章!
您可能感興趣的文章:- 深入解析Go語言的io.ioutil標(biāo)準(zhǔn)庫使用
- Go語言中io.Reader和io.Writer的詳解與實(shí)現(xiàn)
- GO語言的IO方法實(shí)例小結(jié)