在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何使用io.Reader和io.Writer接口在程序中實現流式IO

馬哥Linux運維 ? 來源:思否開發者社區 ? 作者:ronniesong ? 2021-07-29 16:46 ? 次閱讀

Go 語言標準庫 io 包內有一些常用接口和方法,本文配合圖片和實際代碼,詳細介紹了 io 包。

前言

在 Go 中,輸入和輸出操作是使用原語實現的,這些原語將數據模擬成可讀的或可寫的字節流。

為此,Go 的 io 包提供了 io.Reader 和 io.Writer 接口,分別用于數據的輸入和輸出

Go 官方提供了一些 API,支持對內存結構,文件,網絡連接等資源進行操作

本文重點介紹如何實現標準庫中 io.Reader 和 io.Writer 兩個接口,來完成流式傳輸數據。

io.Reader

io.Reader 表示一個讀取器,它將數據從某個資源讀取到傳輸緩沖區。在緩沖區中,數據可以被流式傳輸和使用。

對于要用作讀取器的類型,它必須實現 io.Reader 接口的唯一一個方法 Read(p []byte)。

換句話說,只要實現了 Read(p []byte) ,那它就是一個讀取器。

type Reader interface {

Read(p []byte) (n int, err error)

}

Read() 方法有兩個返回值,一個是讀取到的字節數,一個是發生錯誤時的錯誤。

同時,如果資源內容已全部讀取完畢,應該返回 io.EOF 錯誤。

使用 Reader

利用 Reader 可以很容易地進行流式數據傳輸。Reader 方法內部是被循環調用的,每次迭代,它會從數據源讀取一塊數據放入緩沖區 p (即 Read 的參數 p)中,直到返回 io.EOF 錯誤時停止。

下面是一個簡單的例子,通過 string.NewReader(string) 創建一個字符串讀取器,然后流式地按字節讀取:

func main() {

reader := strings.NewReader(“Clear is better than clever”)

p := make([]byte, 4)

for {

n, err := reader.Read(p)

if err != nil{

if err == io.EOF {

fmt.Println(“EOF:”, n)

break

}

fmt.Println(err)

os.Exit(1)

}

fmt.Println(n, string(p[:n]))

}

}

輸出打印的內容:

4 Clea

4 r is

4 bet

4 ter

4 than

4 cle

3 ver

EOF: 0

可以看到,最后一次返回的 n 值有可能小于緩沖區大小。

自己實現一個 Reader

上一節是使用標準庫中的 io.Reader 讀取器實現的。

現在,讓我們看看如何自己實現一個。它的功能是從流中過濾掉非字母字符。

type alphaReader struct {

// 資源

src string

// 當前讀取到的位置

cur int

}

// 創建一個實例func newAlphaReader(src string) *alphaReader {

return &alphaReader{src: src}

}

// 過濾函數func alpha(r byte) byte {

if (r 》= ‘A’ && r 《= ‘Z’) || (r 》= ‘a’ && r 《= ‘z’) {

return r

}

return 0

}

// Read 方法func (a *alphaReader) Read(p []byte) (int, error) {

// 當前位置 》= 字符串長度 說明已經讀取到結尾 返回 EOF

if a.cur 》= len(a.src) {

return 0, io.EOF

}

// x 是剩余未讀取的長度

x := len(a.src) - a.cur

n, bound := 0, 0

if x 》= len(p) {

// 剩余長度超過緩沖區大小,說明本次可完全填滿緩沖區

bound = len(p)

} else if x 《 len(p) {

// 剩余長度小于緩沖區大小,使用剩余長度輸出,緩沖區不補滿

bound = x

}

buf := make([]byte, bound)

for n 《 bound {

// 每次讀取一個字節,執行過濾函數

if char := alpha(a.src[a.cur]); char != 0 {

buf[n] = char

}

n++

a.cur++

}

// 將處理后得到的 buf 內容復制到 p 中

copy(p, buf)

return n, nil

}

func main() {

reader := newAlphaReader(“Hello! It‘s 9am, where is the sun?”)

p := make([]byte, 4)

for {

n, err := reader.Read(p)

if err == io.EOF {

break

}

fmt.Print(string(p[:n]))

}

fmt.Println()

}

輸出打印的內容:

HelloItsamwhereisthesun

組合多個 Reader,目的是重用和屏蔽下層實現的復雜度

標準庫已經實現了許多 Reader。

使用一個 Reader 作為另一個 Reader 的實現是一種常見的用法。

這樣做可以讓一個 Reader 重用另一個 Reader 的邏輯,下面展示通過更新 alphaReader 以接受 io.Reader 作為其來源。

type alphaReader struct {

// alphaReader 里組合了標準庫的 io.Reader

reader io.Reader

}

func newAlphaReader(reader io.Reader) *alphaReader {

return &alphaReader{reader: reader}

}

func alpha(r byte) byte {

if (r 》= ’A‘ && r 《= ’Z‘) || (r 》= ’a‘ && r 《= ’z‘) {

return r

}

return 0

}

func (a *alphaReader) Read(p []byte) (int, error) {

// 這行代碼調用的就是 io.Reader

n, err := a.reader.Read(p)

if err != nil {

return n, err

}

buf := make([]byte, n)

for i := 0; i 《 n; i++ {

if char := alpha(p[i]); char != 0 {

buf[i] = char

}

}

copy(p, buf)

return n, nil

}

func main() {

// 使用實現了標準庫 io.Reader 接口的 strings.Reader 作為實現

reader := newAlphaReader(strings.NewReader(“Hello! It’s 9am, where is the sun?”))

p := make([]byte, 4)

for {

n, err := reader.Read(p)

if err == io.EOF {

break

}

fmt.Print(string(p[:n]))

}

fmt.Println()

}

這樣做的另一個優點是 alphaReader 能夠從任何 Reader 實現中讀取。

例如,以下代碼展示了 alphaReader 如何與 os.File 結合以過濾掉文件中的非字母字符:

func main() {

// file 也實現了 io.Reader

file, err := os.Open(“。/alpha_reader3.go”)

if err != nil {

fmt.Println(err)

os.Exit(1)

}

defer file.Close()

// 任何實現了 io.Reader 的類型都可以傳入 newAlphaReader

// 至于具體如何讀取文件,那是標準庫已經實現了的,我們不用再做一遍,達到了重用的目的

reader := newAlphaReader(file)

p := make([]byte, 4)

for {

n, err := reader.Read(p)

if err == io.EOF {

break

}

fmt.Print(string(p[:n]))

}

fmt.Println()

}

io.Writer

io.Writer 表示一個編寫器,它從緩沖區讀取數據,并將數據寫入目標資源。

對于要用作編寫器的類型,必須實現 io.Writer 接口的唯一一個方法 Write(p []byte)

同樣,只要實現了 Write(p []byte) ,那它就是一個編寫器。

type Writer interface {

Write(p []byte) (n int, err error)

}

Write() 方法有兩個返回值,一個是寫入到目標資源的字節數,一個是發生錯誤時的錯誤。

使用 Writer

標準庫提供了許多已經實現了 io.Writer 的類型。

下面是一個簡單的例子,它使用 bytes.Buffer 類型作為 io.Writer 將數據寫入內存緩沖區。

func main() {

proverbs := []string{

“Channels orchestrate mutexes serialize”,

“Cgo is not Go”,

“Errors are values”,

“Don‘t panic”,

}

var writer bytes.Buffer

for _, p := range proverbs {

n, err := writer.Write([]byte(p))

if err != nil {

fmt.Println(err)

os.Exit(1)

}

if n != len(p) {

fmt.Println(“failed to write data”)

os.Exit(1)

}

}

fmt.Println(writer.String())

}

輸出打印的內容:

Channels orchestrate mutexes serializeCgo is not GoErrors are valuesDon’t panic

自己實現一個 Writer

下面我們來實現一個名為 chanWriter 的自定義 io.Writer ,它將其內容作為字節序列寫入 channel 。

type chanWriter struct {

// ch 實際上就是目標資源

ch chan byte

}

func newChanWriter() *chanWriter {

return &chanWriter{make(chan byte, 1024)}

}

func (w *chanWriter) Chan() 《-chan byte {

return w.ch

}

func (w *chanWriter) Write(p []byte) (int, error) {

n := 0

// 遍歷輸入數據,按字節寫入目標資源

for _, b := range p {

w.ch 《- b

n++

}

return n, nil

}

func (w *chanWriter) Close() error {

close(w.ch)

return nil

}

func main() {

writer := newChanWriter()

go func() {

defer writer.Close()

writer.Write([]byte(“Stream ”))

writer.Write([]byte(“me!”))

}()

for c := range writer.Chan() {

fmt.Printf(“%c”, c)

}

fmt.Println()

}

要使用這個 Writer,只需在函數 main() 中調用 writer.Write()(在單獨的 goroutine 中)。

因為 chanWriter 還實現了接口 io.Closer ,所以調用方法 writer.Close() 來正確地關閉 channel,以避免發生泄漏和死鎖。

io 包里其他有用的類型和方法

如前所述,Go 標準庫附帶了許多有用的功能和類型,讓我們可以輕松使用流式 io。

os.File

類型 os.File 表示本地系統上的文件。它實現了 io.Reader 和 io.Writer ,因此可以在任何 io 上下文中使用。

例如,下面的例子展示如何將連續的字符串切片直接寫入文件:

func main() {

proverbs := []string{

“Channels orchestrate mutexes serialize

”,

“Cgo is not Go

”,

“Errors are values

”,

“Don‘t panic

”,

}

file, err := os.Create(“。/proverbs.txt”)

if err != nil {

fmt.Println(err)

os.Exit(1)

}

defer file.Close()

for _, p := range proverbs {

// file 類型實現了 io.Writer

n, err := file.Write([]byte(p))

if err != nil {

fmt.Println(err)

os.Exit(1)

}

if n != len(p) {

fmt.Println(“failed to write data”)

os.Exit(1)

}

}

fmt.Println(“file write done”)

}

同時,io.File 也可以用作讀取器來從本地文件系統讀取文件的內容。

例如,下面的例子展示了如何讀取文件并打印其內容:

func main() {

file, err := os.Open(“。/proverbs.txt”)

if err != nil {

fmt.Println(err)

os.Exit(1)

}

defer file.Close()

p := make([]byte, 4)

for {

n, err := file.Read(p)

if err == io.EOF {

break

}

fmt.Print(string(p[:n]))

}

}

標準輸入、輸出和錯誤

os 包有三個可用變量 os.Stdout ,os.Stdin 和 os.Stderr ,它們的類型為 *os.File,分別代表 系統標準輸入,系統標準輸出 和 系統標準錯誤 的文件句柄。

例如,下面的代碼直接打印到標準輸出:

func main() {

proverbs := []string{

“Channels orchestrate mutexes serialize

”,

“Cgo is not Go

”,

“Errors are values

”,

“Don’t panic

”,

}

for _, p := range proverbs {

// 因為 os.Stdout 也實現了 io.Writer

n, err := os.Stdout.Write([]byte(p))

if err != nil {

fmt.Println(err)

os.Exit(1)

}

if n != len(p) {

fmt.Println(“failed to write data”)

os.Exit(1)

}

}

}

io.Copy()

io.Copy() 可以輕松地將數據從一個 Reader 拷貝到另一個 Writer。

它抽象出 for 循環模式(我們上面已經實現了)并正確處理 io.EOF 和 字節計數。

下面是我們之前實現的簡化版本:

func main() {

proverbs := new(bytes.Buffer)

proverbs.WriteString(“Channels orchestrate mutexes serialize

”)

proverbs.WriteString(“Cgo is not Go

”)

proverbs.WriteString(“Errors are values

”)

proverbs.WriteString(“Don‘t panic

”)

file, err := os.Create(“。/proverbs.txt”)

if err != nil {

fmt.Println(err)

os.Exit(1)

}

defer file.Close()

// io.Copy 完成了從 proverbs 讀取數據并寫入 file 的流程

if _, err := io.Copy(file, proverbs); err != nil {

fmt.Println(err)

os.Exit(1)

}

fmt.Println(“file created”)

}

那么,我們也可以使用 io.Copy() 函數重寫從文件讀取并打印到標準輸出的先前程序,如下所示:

func main() {

file, err := os.Open(“。/proverbs.txt”)

if err != nil {

fmt.Println(err)

os.Exit(1)

}

defer file.Close()

if _, err := io.Copy(os.Stdout, file); err != nil {

fmt.Println(err)

os.Exit(1)

}

}

io.WriteString()

此函數讓我們方便地將字符串類型寫入一個 Writer:

func main() {

file, err := os.Create(“。/magic_msg.txt”)

if err != nil {

fmt.Println(err)

os.Exit(1)

}

defer file.Close()

if _, err := io.WriteString(file, “Go is fun!”); err != nil {

fmt.Println(err)

os.Exit(1)

}

}

使用管道的 Writer 和 Reader

類型 io.PipeWriter 和 io.PipeReader 在內存管道中模擬 io 操作。

數據被寫入管道的一端,并使用單獨的 goroutine 在管道的另一端讀取。

下面使用 io.Pipe() 創建管道的 reader 和 writer,然后將數據從 proverbs 緩沖區復制到io.Stdout :

func main() {

proverbs := new(bytes.Buffer)

proverbs.WriteString(“Channels orchestrate mutexes serialize

”)

proverbs.WriteString(“Cgo is not Go

”)

proverbs.WriteString(“Errors are values

”)

proverbs.WriteString(“Don’t panic

”)

piper, pipew := io.Pipe()

// 將 proverbs 寫入 pipew 這一端

go func() {

defer pipew.Close()

io.Copy(pipew, proverbs)

}()

// 從另一端 piper 中讀取數據并拷貝到標準輸出

io.Copy(os.Stdout, piper)

piper.Close()

}

緩沖區 io

標準庫中 bufio 包支持 緩沖區 io 操作,可以輕松處理文本內容。

例如,以下程序逐行讀取文件的內容,并以值 ‘ ’ 分隔:

func main() {

file, err := os.Open(“。/planets.txt”)

if err != nil {

fmt.Println(err)

os.Exit(1)

}

defer file.Close()

reader := bufio.NewReader(file)

for {

line, err := reader.ReadString(‘

’)

if err != nil {

if err == io.EOF {

break

} else {

fmt.Println(err)

os.Exit(1)

}

}

fmt.Print(line)

}

}

ioutil

io 包下面的一個子包 utilio 封裝了一些非常方便的功能

例如,下面使用函數 ReadFile 將文件內容加載到 []byte 中。

package main

import (

“io/ioutil”

。。。

func main() {

bytes, err := ioutil.ReadFile(“。/planets.txt”)

if err != nil {

fmt.Println(err)

os.Exit(1)

}

fmt.Printf(“%s”, bytes)

}

總結

本文介紹了如何使用 io.Reader 和 io.Writer 接口在程序中實現流式 IO。閱讀本文后,您應該能夠了解如何使用 io 包來實現 流式傳輸 IO 數據的程序。

其中有一些例子,展示了如何創建自己的類型,并實現io.Reader 和 io.Writer 。

這是一個簡單介紹性質的文章,沒有擴展開來講。

例如,我們沒有深入文件 IO,緩沖 IO,網絡 IO 或格式化 IO(保存用于將來的寫入)。

我希望這篇文章可以讓你了解 Go 語言中 流式 IO 的常見用法是什么。

謝謝!

轉自:ronniesong

segmentfault.com/a/1190000015591319

編輯:jq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Writer
    +關注

    關注

    0

    文章

    8

    瀏覽量

    7333

原文標題:Go 中 io 包的使用方法

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    λ-IO:存儲計算下的IO棧設計

    動機和背景? ? 存儲計算存儲資源的充分利用。IO棧是管理存儲器的的基本組件,包括設備驅動、塊接口層、文件系統,目前一些用戶空間IO庫(如SPDK)有效降低了延遲,但是io棧仍然不可或
    的頭像 發表于 12-02 10:35 ?209次閱讀
    λ-<b class='flag-5'>IO</b>:存儲計算下的<b class='flag-5'>IO</b>棧設計

    MR20遠程IOIO-Link的差異化應用

    工業自動化領域,遠程IO)和IO-Link作為兩種重要的通信協議,各自扮演著不可或缺的角色。盡管它們都是為了實現設備之間的數據傳輸與控制,但在應用場景、系統架構和功能特點上卻存在著顯
    的頭像 發表于 10-21 17:29 ?221次閱讀

    本地IO與遠程IO:揭秘工業自動化的兩大關鍵角色

    工業自動化領域,IO(Input/Output,輸入/輸出)模塊扮演著至關重要的角色。它們作為連接控制系統與現場設備的橋梁,負責數據的采集與指令的執行。然而,隨著技術的不斷進步,IO模塊也分為本地
    的頭像 發表于 10-08 18:06 ?408次閱讀

    IO-Link的定義和特點

    IO-Link是一種用于工業自動化領域的數字通信協議和接口標準,它允許傳感器、執行器和其他工業設備與控制器(如PLC等)之間進行雙向通信,以實現實時數據傳輸和控制信號交換。以下是對IO
    的頭像 發表于 10-08 11:23 ?1019次閱讀

    io口和串口的區別 單片機有多少個io

    等。而串口,即串行通信接口(Serial Communication Interface),是一種數據通信方式,通過一條數據線按照順序傳送數據。IO口和串口功能和用途上存在顯著區別: 通信方式 :
    的頭像 發表于 10-06 10:06 ?1508次閱讀

    MCU IO口的作用和特點

    MCU(微控制器)的IO口(Input/Output Port,輸入輸出端口)是單片機與外界進行信息交互的關鍵接口。這些IO微控制器的功能實現
    的頭像 發表于 09-30 11:52 ?832次閱讀

    使用IO-link主幀處理程序實現靈活的時序配置

    電子發燒友網站提供《使用IO-link主幀處理程序實現靈活的時序配置.pdf》資料免費下載
    發表于 09-19 11:28 ?0次下載
    使用<b class='flag-5'>IO</b>-link主幀處理<b class='flag-5'>程序</b><b class='flag-5'>實現</b>靈活的時序配置

    遠程IO實現設備間高效通信與控制的橋梁

    在當今數字化時代,遠程IO(輸入/輸出)技術已成為實現工業自動化、智慧城市等系統不可或缺的一部分。那么,遠程IO究竟是什么?它又是如何工作的呢?今天,我將帶您探索遠程
    的頭像 發表于 09-06 17:22 ?396次閱讀
    遠程<b class='flag-5'>IO</b>:<b class='flag-5'>實現</b>設備間高效通信與控制的橋梁

    初識IO-Link及IO-Link設備軟件協議棧

    軟件堆疊套件包括亞信IO-Link 設備軟件協議棧試用庫、IO-Link傳感器驅動程序以及演示應用程序
    的頭像 發表于 07-08 13:55 ?2621次閱讀
    初識<b class='flag-5'>IO</b>-Link及<b class='flag-5'>IO</b>-Link設備軟件協議棧

    PLC之間的IO交互怎么實現

    工業自動化領域,可編程邏輯控制器(PLC)作為核心控制設備,其重要性不言而喻。PLC之間的IO交互是實現自動化生產線高效、穩定運行的關鍵環節。本文將詳細探討PLC之間IO交互的
    的頭像 發表于 06-17 11:09 ?1921次閱讀

    PLC IO接口的功能及使用方法

    工業自動化和控制系統設計,PLC(Programmable Logic Controller,可編程邏輯控制器)作為核心控制單元,其IO(Input/Output,輸入/輸出)接口
    的頭像 發表于 06-15 16:49 ?2126次閱讀

    遠程IO與分布式IO的區別

    工業自動化和控制系統設計,遠程IO(Input/Output)和分布式IO是兩個重要的概念。它們各自具有獨特的特點和優勢,適用于不同的應用場景。本文將詳細探討遠程
    的頭像 發表于 06-15 15:57 ?2567次閱讀

    使用IO-Link技術能帶來哪些優勢?細數IO-Link八大優勢

    IO-Link是國際標準化的跨供應商IO技術,能夠實現從控制系統到傳感器/執行器級別的雙向通信。
    的頭像 發表于 03-08 13:40 ?1011次閱讀

    EtherCAT IO的接線方法和流程是怎樣的?

    EtherCAT IO的接線方法和流程是怎樣的? EtherCAT是一種用于實時以太網通信的開放式通信協議,具有低延遲和高帶寬的優勢。 EtherCAT IO是EtherCAT網絡連接到IO
    的頭像 發表于 02-02 16:57 ?2069次閱讀

    什么是io多路復用?IO多路復用的優缺點

    IO多路復用是一種同步IO模型,它允許單個進程/線程同時處理多個IO請求。具體來說,一個進程/線程可以監視多個文件句柄,一旦某個文件句柄就緒,就能夠通知應用程序進行相應的讀寫操作。
    的頭像 發表于 01-18 15:48 ?1663次閱讀
    主站蜘蛛池模板: 天堂最新版中文网| 色婷婷六月| 看免费一级片| 久久久久久午夜精品| 久久精品国产99精品国产2021| 久久本道综合色狠狠五月| 天堂资源在线观看| 亚洲伦理一区| 国产精品美女www爽爽爽视频| 一级毛片在线播放| 激情综合婷婷丁香六月花| 碰免费人人人视频| 天堂电影免费在线观看| 视频色版| 亚洲天堂资源网| 欧美成人亚洲| 91啦中文在线观看| 91无毒不卡| 九九热精品在线| 欧美性幼| 欧美性猛交xxx嘿人猛交| 中文字幕在线一区| 欧美在线黄色| 视频在线二区| 日本一区二区视频| 亚洲欧美日韩特级毛片| 99精品热| 在线观看亚洲人成网站| 美女视频一区二区三区| 亚洲第一区视频| 亚洲日本色图| 欧美日韩中文字幕在线| 欧美色图 亚洲| 国产精品igao在线观看樱花日本 | 奇米7777影视| 天天摸天天草| 午夜视频在线观看免费视频| 97色婷婷成人综合在线观看| 国产高清在线免费| 神马午夜51| 黄a大片|