91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

golang怎么通過io包進行文件讀寫

發布時間:2023-04-26 10:01:56 來源:億速云 閱讀:112 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“golang怎么通過io包進行文件讀寫”,內容詳細,步驟清晰,細節處理妥當,希望這篇“golang怎么通過io包進行文件讀寫”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

在 golang 語言中,實現io的讀與寫,只要實現了如下的讀寫接口即可:

// 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 語言自身提供了很多實現這兩個接口的結構體,比較典型的有:io、os、bytes.buffer、strings、bufio等,這些解決了文件的讀寫,字節、字符串的讀寫,或者是帶緩沖的讀寫等。

今天我們先來看看 io包 中提供的這些接口,簡單了解下相關原理。

1.io包的讀寫函數

典型的相關函數聲明如下:

// 讀操作
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() 函數中,接收一個 Reader 類型的參數,比如 os.File 類型,又或者是其他的實現了 io.Reader 接口的結構體類型,輸出讀取到的內容,以字節數組形式輸出,外附一個 error 錯誤,我們進一步看看其內部實現:

func ReadAll(r Reader) ([]byte, error) {
        // 新建字節數組,cap=512
	b := make([]byte, 0, 512)
        // 開啟循環讀取內容
	for {
                // 
		if len(b) == cap(b) {
			// Add more capacity (let append pick how much).
			b = append(b, 0)[:len(b)]
		}
                // 讀取 reader中的內容,填充到對應部分
		n, err := r.Read(b[len(b):cap(b)])
		b = b[:len(b)+n]
                // 如果讀到了結尾,可以返回數據
		if err != nil {
			if err == EOF {
				err = nil
			}
			return b, err
		}
	}
}

從上面源碼可以知道,io.ReadAll() 函數,通過一次讀取 Reader 中的所有內容,如果是小文件無所謂,占用內存有限,但如果是好幾個G的文件呢,是不是,本來服務器內存有限,以下占用幾G內存,這樣讀取不太合理,當然,golang 也為我們提供了帶緩沖的讀取,這是后話,后面再討論。

接下來看看寫入操作的源碼實現:

// io.Copy()
func Copy(dst Writer, src Reader) (written int64, err error) {
	return copyBuffer(dst, src, nil) // 實際通過調用此函數實現
}

func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
	if buf != nil && len(buf) == 0 { // 當buf非空或者長度0,panic
		panic("empty buffer in CopyBuffer")
	}
	return copyBuffer(dst, src, buf) // 繼續往下調用
}


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 { // 源實現了writeTo就可以直接調用結束
		return wt.WriteTo(dst)
	}
	// Similarly, if the writer has a ReadFrom method, use it to do the copy.
	if rt, ok := dst.(ReaderFrom); ok { // 目標實現了readFrom,也可以直接調用結束
		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空時,創建buf
	}
        // 開啟循環內容讀取
	for {
		nr, er := src.Read(buf) // 從源中讀取內容到buf中
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr]) // 目標w從buf中寫入內容
			if nw < 0 || nr < nw {
				nw = 0
				if ew == nil {
					ew = errInvalidWrite
				}
			}
			written += int64(nw) // 更新寫入長度
			if ew != nil { // 寫入返回err,退出循環
				err = ew
				break
			}
			if nr != nw { // 讀取與寫入長度不等,退出
				err = ErrShortWrite
				break
			}
		}
		if er != nil { // 讀err非空,退出
			if er != EOF {
				err = er
			}
			break
		}
	}
	return written, err // 返回寫入長度及err
}

通過源碼走讀,我們可以看到,在函數中傳入 Writer 作為目標寫入對象,傳入 Reader 作為源讀取對象,如果 Reader 對象實現 WriteTo 方法,我們可以直接調用此完成 copy, 或者是 Writer 對象實現了 ReadFrom 方法,我們可以直接調用此完成 copy,都沒有實現,就只有通過 buf 作為中轉,通過循環,先從源讀取內容,再寫入目標對象中,最后返回整個內容的長度,當然其中也有相關錯誤處理,這里不做討論。

接下來 io包 中提供的另外的寫入操作實現:

// io.WriteString()
func WriteString(w Writer, s string) (n int, err error) {
	if sw, ok := w.(StringWriter); ok { // 如w實現了 StringWriter,則調用相關方法實現寫入
		return sw.WriteString(s)
	}
	return w.Write([]byte(s)) // 作為兜底,w對象是肯定實現了 Write() 方法,所以調用此方法實現寫入
}

從上面代碼可以看到,該實現邏輯簡單,具體見注釋。

io包實現讀寫操作 準備:

  • file.txt,作為讀取源,然后再寫入其他文件中 寫操作,我們實現了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)
}

2.io.ioutil包實現讀寫操作

所謂 util,肯定是作為工具使用,怎么方便怎么來,我們甚至通過文件名就可以完成操作,具體實現細節我們不關心,總之它實現了這些功能。

注意 由于我使用的 go 1.19,在源碼中,明確聲明在 go 1.16 后,逐漸棄用了,所以使用高版本的 go 時要注意這個問題,這里為了增加源碼的了解熟悉,也拿出來作為分享。

讀操作

// 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 中,實際也是調用上面的 io.ReadAll(),這里不贅述,在 ReadFile 中,我們傳入個文件名,就可以讀取到整個字節內容,實際它也是調用 os.ReadFile() 實現,后面再討論 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)
}

可以看到,這里也是調用了 os.WriteFile() 實現文件的寫入,只需要我們傳入待寫入的文件名,寫入的字節數組,以及寫入文件的權限,是不是很簡單。

代碼實用

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包進行文件讀寫”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

筠连县| 清水河县| 印江| 鄢陵县| 武汉市| 长丰县| 新巴尔虎右旗| 青田县| 黔东| 邵武市| 华阴市| 马山县| 石景山区| 玉龙| 抚州市| 延边| 盖州市| 三台县| 嘉义县| 兰西县| 资中县| 文昌市| 平南县| 谷城县| 建始县| 石城县| 平顺县| 耿马| 奇台县| 岐山县| 望谟县| 广安市| 赣榆县| 密云县| 罗平县| 吉首市| 康保县| 湘乡市| 财经| 诏安县| 丰宁|