您好,登錄后才能下訂單哦!
本文小編為大家詳細(xì)介紹“golang怎么通過io包進(jìn)行文件讀寫”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“golang怎么通過io包進(jìn)行文件讀寫”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。
在 golang
語言中,實(shí)現(xiàn)io的讀與寫,只要實(shí)現(xiàn)了如下的讀寫接口即可:
// go 1.19/src/io/io.go type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) }
golang
語言自身提供了很多實(shí)現(xiàn)這兩個(gè)接口的結(jié)構(gòu)體,比較典型的有:io、os、bytes.buffer、strings、bufio
等,這些解決了文件的讀寫,字節(jié)、字符串的讀寫,或者是帶緩沖的讀寫等。
今天我們先來看看 io包
中提供的這些接口,簡單了解下相關(guān)原理。
典型的相關(guān)函數(shù)聲明如下:
// 讀操作 func ReadAll(r Reader) ([]byte, error) {...} // 寫操作 func Copy(dst Writer, src Reader) (written int64, err error) {...} func WriteString(w Writer, s string) (n int, err error) {...}
在上面的 ReadAll()
函數(shù)中,接收一個(gè) Reader
類型的參數(shù),比如 os.File
類型,又或者是其他的實(shí)現(xiàn)了 io.Reader
接口的結(jié)構(gòu)體類型,輸出讀取到的內(nèi)容,以字節(jié)數(shù)組形式輸出,外附一個(gè) error
錯誤,我們進(jìn)一步看看其內(nèi)部實(shí)現(xiàn):
func ReadAll(r Reader) ([]byte, error) { // 新建字節(jié)數(shù)組,cap=512 b := make([]byte, 0, 512) // 開啟循環(huán)讀取內(nèi)容 for { // if len(b) == cap(b) { // Add more capacity (let append pick how much). b = append(b, 0)[:len(b)] } // 讀取 reader中的內(nèi)容,填充到對應(yīng)部分 n, err := r.Read(b[len(b):cap(b)]) b = b[:len(b)+n] // 如果讀到了結(jié)尾,可以返回?cái)?shù)據(jù) if err != nil { if err == EOF { err = nil } return b, err } } }
從上面源碼可以知道,io.ReadAll()
函數(shù),通過一次讀取 Reader
中的所有內(nèi)容,如果是小文件無所謂,占用內(nèi)存有限,但如果是好幾個(gè)G的文件呢,是不是,本來服務(wù)器內(nèi)存有限,以下占用幾G內(nèi)存,這樣讀取不太合理,當(dāng)然,golang
也為我們提供了帶緩沖的讀取,這是后話,后面再討論。
接下來看看寫入操作的源碼實(shí)現(xiàn):
// io.Copy() func Copy(dst Writer, src Reader) (written int64, err error) { return copyBuffer(dst, src, nil) // 實(shí)際通過調(diào)用此函數(shù)實(shí)現(xiàn) } func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { if buf != nil && len(buf) == 0 { // 當(dāng)buf非空或者長度0,panic panic("empty buffer in CopyBuffer") } return copyBuffer(dst, src, buf) // 繼續(xù)往下調(diào)用 } func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { // If the reader has a WriteTo method, use it to do the copy. // Avoids an allocation and a copy. if wt, ok := src.(WriterTo); ok { // 源實(shí)現(xiàn)了writeTo就可以直接調(diào)用結(jié)束 return wt.WriteTo(dst) } // Similarly, if the writer has a ReadFrom method, use it to do the copy. if rt, ok := dst.(ReaderFrom); ok { // 目標(biāo)實(shí)現(xiàn)了readFrom,也可以直接調(diào)用結(jié)束 return rt.ReadFrom(src) } if buf == nil { size := 32 * 1024 if l, ok := src.(*LimitedReader); ok && int64(size) > l.N { if l.N < 1 { size = 1 } else { size = int(l.N) } } buf = make([]byte, size) // buf空時(shí),創(chuàng)建buf } // 開啟循環(huán)內(nèi)容讀取 for { nr, er := src.Read(buf) // 從源中讀取內(nèi)容到buf中 if nr > 0 { nw, ew := dst.Write(buf[0:nr]) // 目標(biāo)w從buf中寫入內(nèi)容 if nw < 0 || nr < nw { nw = 0 if ew == nil { ew = errInvalidWrite } } written += int64(nw) // 更新寫入長度 if ew != nil { // 寫入返回err,退出循環(huán) err = ew break } if nr != nw { // 讀取與寫入長度不等,退出 err = ErrShortWrite break } } if er != nil { // 讀err非空,退出 if er != EOF { err = er } break } } return written, err // 返回寫入長度及err }
通過源碼走讀,我們可以看到,在函數(shù)中傳入 Writer
作為目標(biāo)寫入對象,傳入 Reader
作為源讀取對象,如果 Reader
對象實(shí)現(xiàn) WriteTo
方法,我們可以直接調(diào)用此完成 copy
, 或者是 Writer
對象實(shí)現(xiàn)了 ReadFrom
方法,我們可以直接調(diào)用此完成 copy
,都沒有實(shí)現(xiàn),就只有通過 buf
作為中轉(zhuǎn),通過循環(huán),先從源讀取內(nèi)容,再寫入目標(biāo)對象中,最后返回整個(gè)內(nèi)容的長度,當(dāng)然其中也有相關(guān)錯誤處理,這里不做討論。
接下來 io包
中提供的另外的寫入操作實(shí)現(xiàn):
// io.WriteString() func WriteString(w Writer, s string) (n int, err error) { if sw, ok := w.(StringWriter); ok { // 如w實(shí)現(xiàn)了 StringWriter,則調(diào)用相關(guān)方法實(shí)現(xiàn)寫入 return sw.WriteString(s) } return w.Write([]byte(s)) // 作為兜底,w對象是肯定實(shí)現(xiàn)了 Write() 方法,所以調(diào)用此方法實(shí)現(xiàn)寫入 }
從上面代碼可以看到,該實(shí)現(xiàn)邏輯簡單,具體見注釋。
io包實(shí)現(xiàn)讀寫操作
準(zhǔn)備:
file.txt,作為讀取源,然后再寫入其他文件中 寫操作,我們實(shí)現(xiàn)了2種,一種 io.Copy()
,一種 io.WriteString()
,具體使用見下面代碼:
func ioRW() { // read f, err := os.Open("D:\\demo1\\src\\demo23\\go-io\\file\\file.txt") defer f.Close() if err != nil { fmt.Printf("err: %s\n", err) return } b, err := io.ReadAll(f) if err != nil { fmt.Printf("err: %s\n", err) return } fmt.Printf("ioRW read content: \n%s\n", b) // write // 1.io.Copy() fw, err := os.Create("io_w.txt") defer fw.Close() n, err := io.Copy(fw, strings.NewReader(string(b))) if err != nil { fmt.Printf("err: %s\n", err) return } // 2.io.WriteString() fw_1, _ := os.Create("io_w_1.txt") defer fw_1.Close() n, err := io.WriteString(fw_1, string(b)) if err != nil { fmt.Printf("err: %s\n", err) return } fmt.Printf("ioRW write size: %d\n", n) }
所謂 util
,肯定是作為工具使用,怎么方便怎么來,我們甚至通過文件名就可以完成操作,具體實(shí)現(xiàn)細(xì)節(jié)我們不關(guān)心,總之它實(shí)現(xiàn)了這些功能。
注意
由于我使用的 go 1.19
,在源碼中,明確聲明在 go 1.16
后,逐漸棄用了,所以使用高版本的 go
時(shí)要注意這個(gè)問題,這里為了增加源碼的了解熟悉,也拿出來作為分享。
讀操作
// Deprecated: As of Go 1.16, this function simply calls io.ReadAll. func ReadAll(r io.Reader) ([]byte, error) { return io.ReadAll(r) } // Deprecated: As of Go 1.16, this function simply calls os.ReadFile. func ReadFile(filename string) ([]byte, error) { return os.ReadFile(filename) }
在 ReadAll
中,實(shí)際也是調(diào)用上面的 io.ReadAll()
,這里不贅述,在 ReadFile
中,我們傳入個(gè)文件名,就可以讀取到整個(gè)字節(jié)內(nèi)容,實(shí)際它也是調(diào)用 os.ReadFile()
實(shí)現(xiàn),后面再討論 os包
。
寫操作
// Deprecated: As of Go 1.16, this function simply calls os.WriteFile. func WriteFile(filename string, data []byte, perm fs.FileMode) error { return os.WriteFile(filename, data, perm) }
可以看到,這里也是調(diào)用了 os.WriteFile()
實(shí)現(xiàn)文件的寫入,只需要我們傳入待寫入的文件名,寫入的字節(jié)數(shù)組,以及寫入文件的權(quán)限,是不是很簡單。
代碼實(shí)用
func ioutilRW() { // read b, err := ioutil.ReadFile("D:\\demo1\\src\\demo23\\go-io\\file\\file.txt") if err != nil { fmt.Printf("err: %s\n", err) return } fmt.Printf("ioutilRW read content: \n%s\n", b) // write err = ioutil.WriteFile("ioutilRW_w.txt", b, 664) if err != nil { fmt.Printf("err: %s\n", err) return } fmt.Println("ioutilRW write err: ", err) }
讀到這里,這篇“golang怎么通過io包進(jìn)行文件讀寫”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點(diǎn)還需要大家自己動手實(shí)踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。