您好,登錄后才能下訂單哦!
今天小編給大家分享一下Go語言中的Writer和Reader怎么使用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
輸入和輸出
Go Writer 和 Reader接口的設計遵循了Unix的輸入和輸出,一個程序的輸出可以是另外一個程序的輸入。他們的功能單一并且純粹,這樣就可以非常容易的編寫程序代碼,又可以通過組合的概念,讓我們的程序做更多的事情。
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
這三種標準的輸入和輸出都是一個*File,而*File恰恰就是同時實現了io.Writer和io.Reader這兩個接口的類型,所以它們同時具備輸入和輸出的功能,既可以從里面讀取數據,又可以往里面寫入數據。
Go標準庫的io包也是基于Unix這種輸入和輸出的理念,大部分的接口都是擴展了io.Writer和io.Reader,大部分的類型也都選擇地實現了io.Writer和io.Reader這兩個接口,然后把數據的輸入和輸出,抽象為流的讀寫。所以只要實現了這兩個接口,都可以使用流的讀寫功能。
io.Writer和io.Reader兩個接口的高度抽象,讓我們不用再面向具體的業務,我們只關注,是讀還是寫。只要我們定義的方法函數可以接收這兩個接口作為參數,那么我們就可以進行流的讀寫,而不用關心如何讀、寫到哪里去,這也是面向接口編程的好處。
Reader和Writer接口
這兩個高度抽象的接口,只有一個方法,也體現了Go接口設計的簡潔性,只做一件事。
// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
// Write must not modify the slice data, even temporarily.
//
// Implementations must not retain p.
type Writer interface {
Write(p []byte) (n int, err error)
}
這是Wirter接口的定義,它只有一個Write方法。它接受一個byte的切片,返回兩個值,n表示寫入的字節數、err表示寫入時發生的錯誤。
從其文檔注釋來看,這個方法是有規范要求的,我們要想實現一個io.Writer接口,就要遵循這些規則。
write方法向底層數據流寫入len(p)字節的數據,這些數據來自于切片p;
返回被寫入的字節數n,0 <= n <= len(p);
如果n<len(p), 則必須返回一些非nil的err;
如果中途出現問題,也要返回非nil的err;
Write方法絕對不能修改切片p以及里面的數據。
這些實現io.Writer接口的規則,所有實現了該接口的類型都要遵守,不然可能會導致莫名其妙的問題。
// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p. It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered. Even if Read
// returns n < len(p), it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available instead of waiting for more.
//
// When Read encounters an error or end-of-file condition after
// successfully reading n > 0 bytes, it returns the number of
// bytes read. It may return the (non-nil) error from the same call
// or return the error (and n == 0) from a subsequent call.
// An instance of this general case is that a Reader returning
// a non-zero number of bytes at the end of the input stream may
// return either err == EOF or err == nil. The next Read should
// return 0, EOF.
//
// Callers should always process the n > 0 bytes returned before
// considering the error err. Doing so correctly handles I/O errors
// that happen after reading some bytes and also both of the
// allowed EOF behaviors.
//
// Implementations of Read are discouraged from returning a
// zero byte count with a nil error, except when len(p) == 0.
// Callers should treat a return of 0 and nil as indicating that
// nothing happened; in particular it does not indicate EOF.
//
// Implementations must not retain p.
type Reader interface {
Read(p []byte) (n int, err error)
}
這是io.Reader接口定義,也只有一個Read方法。這個方法接受一個byte的切片,并返回兩個值,一個是讀入的字節數,一個是err錯誤。
從其注釋文檔看,io.Reader接口的規則更多。
Read最多讀取len(p)字節的數據,并保存到p;
返回讀取的字節數以及任何發生的錯誤信息;
n要滿足0 <= n <= len(p);
n<len(p)時,表示讀取的數據不足以填滿p,這時方法會立即返回,而不是等待更多的數據;
讀取過程中遇到錯誤,會返回讀取的字節數n以及相應的錯誤err;
在底層輸入流結束時,方法會返回n>0的字節,但是err可能時EOF,也可以是nil;
在第6種(上面)情況下,再次調用read方法的時候,肯定會返回0,EOF;
調用Read方法時,如果n>0時,優先處理處理讀入的數據,然后再處理錯誤err,EOF也要這樣處理;
Read方法不鼓勵返回n=0并且err=nil的情況。
規則稍微比Write接口有點多,不過也都比較好理解。注意第 8 條,即使我們在讀取的時候遇到錯誤,但是也應該處理已經讀到的數據。因為這些已經讀到的數據是正確的,如果不進行處理丟失的話,讀到的數據就不完整了。
示例
對這兩個接口了解后,我們就可以嘗試使用他們了,現在來看個例子。
func main() {
//定義零值Buffer類型變量b
var b bytes.Buffer
//使用Write方法為寫入字符串
b.Write([]byte("你好"))
//這個是把一個字符串拼接到Buffer里
fmt.Fprint(&b,",","http://www.flysnow.org")
//把Buffer里的內容打印到終端控制臺
b.WriteTo(os.Stdout)
}
這個例子是拼接字符串到Buffer里,然后再輸出到控制臺。它非常簡單,但是利用了流的讀寫,bytes.Buffer是一個可變字節的類型,可以讓我們很容易的對字節進行操作,比如讀寫、追加等。bytes.Buffer實現了io.Writer和io.Reader接口,所以我們可以很容易地進行讀寫操作,而不用關注具體實現。
b.Write([]byte("你好"))實現了寫入一個字符串。我們把這個字符串轉為一個字節切片,然后調用Write方法寫入,這個就是bytes.Buffer為了實現io.Writer接口而實現的一個方法,可以幫我們寫入數據流。
func (b *Buffer) Write(p []byte) (n int, err error) {
b.lastRead = opInvalid
m := b.grow(len(p))
return copy(b.buf[m:], p), nil
}
以上就是bytes.Buffer實現io.Writer接口的方法。最終我們看到,寫入的切片會被拷貝到b.buf里,這里b.buf[m:]拷貝其實就是追加的意思,不會覆蓋已經存在的數據。
從實現看,我們發現其實只有b *Buffer指針實現了io.Writer接口,所以我們示例代碼中調用fmt.Fprint函數的時候,傳遞的是一個地址&b。
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrint(a)
n, err = w.Write(p.buf)
p.free()
return
}
這是函數fmt.Fprint的實現,它的功能就是實現把數據a寫入到一個io.Writer接口,具體如何寫入,它是不關心的。因為這都是io.Writer會做的,它只關心可以寫入即可。w.Write(p.buf)調用Wirte方法寫入。
最后的b.WriteTo(os.Stdout)是把最終的數據輸出到標準的os.Stdout里,以便我們查看輸出,它接收一個io.Writer接口類型的參數。開篇我們講過os.Stdout也實現了這個io.Writer接口,所以就可以作為參數傳入。
這里我們會發現,很多方法的接收參數都是io.Writer接口,當然還有io.Reader接口,這就是面向接口的編程。我們不用關注具體實現,只關注這個接口可以做什么事情。如果我們換成輸出到文件里,那也很容易,只需把os.File類型作為參數即可。任何實現了該接口的類型,都可以作為參數。
除了b.WriteTo方法外,我們還可以使用io.Reader接口的Read方法實現數據的讀取。
var p [100]byte
n,err:=b.Read(p[:])
fmt.Println(n,err,string(p[:n]))
這是最原始的方法,使用Read方法,n為讀取的字節數,然后我們輸出打印出來。
因為byte.Buffer指針實現了io.Reader接口,所以我們還可以使用如下方式讀取數據信息。
data,err:=ioutil.ReadAll(&b)
fmt.Println(string(data),err)
ioutil.ReadAll接收了一個io.Reader接口的參數,表明可以從任何實現了io.Reader接口的類型里讀取全部的數據。
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
buf := bytes.NewBuffer(make([]byte, 0, capacity))
// If the buffer overflows, we will get bytes.ErrTooLarge.
// Return that as an error. Any other panic remains.
defer func() {
e := recover()
if e == nil {
return
}
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
err = panicErr
} else {
panic(e)
}
}()
_, err = buf.ReadFrom(r) return buf.Bytes(), err
}
以上就是“Go語言中的Writer和Reader怎么使用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。