Golang学习 - io 包
本文内容纲要:
------------------------------------------------------------
先说一下接口,Go语言中的接口很简单,在Go语言的io包中有这样一个函数:
funcReadFull(rReader,buf[]byte)(nint,errerror)
这个函数可以把对象r中的数据读出来,然后存入一个缓冲区buf中,以便其它代码可以处理buf中的数据。
这里有个问题,ReadFull函数究竟可以读取哪些对象的数据?可以读文件中的数据吗?可以读网络中的数据吗?可以读数据库中的数据吗?可以读磁盘中的扇区吗?可以读内存中的数据吗?
答案是ReadFull可以读取任何对象的数据,但是有个前提,就是这个对象必须符合Reader的标准。
Reader的标准是什么呢?下面是Reader的定义:
typeReaderinterface{
Read(p[]byte)(nint,errerror)
}
从上面的定义可以看出,Reader的标准很简单,只要某个对象实现了Read方法,这个对象就符合了Reader的标准,就可以被ReadFull读取。
太简单了,只需要实现Read方法,不需要做其它任何事情。下面我们就来定义一个自己的类型,然后实现Read方法:
------------------------------
//定义一个Ustr类型
typeUstrstruct{
sstring//数据流
iint//读写位置
}
//根据字符串创建Ustr对象
funcNewUstr(sstring)*Ustr{
return&Ustr{s,0}
}
//获取未读取部分的数据长度
func(s*Ustr)Len()int{
returnlen(s.s)-s.i
}
//实现Ustr类型的Read方法
func(s*Ustr)Read(p[]byte)(nint,errerror){
for;s.i<len(s.s)&&n<len(p);s.i++{
c:=s.s[s.i]
//将小写字母转换为大写字母,然后写入p中
if'a'<=c&&c<='z'{
p[n]=c+'A'-'a'
}else{
p[n]=c
}
n++
}
//根据读取的字节数设置返回值
ifn==0{
returnn,io.EOF
}
returnn,nil
}
------------------------------
接下来,我们就可以用ReadFull方法读取Ustr对象的数据了:
------------------------------
funcmain(){
s:=NewUstr("HelloWorld!")//创建Ustr对象s
buf:=make([]byte,s.Len())//创建缓冲区buf
n,err:=io.ReadFull(s,buf)//将s中的数据读取到buf中
fmt.Printf("%s\n",buf)//HELLOWORLD!
fmt.Println(n,err)//12<nil>
}
------------------------------
我们很快就实现了Reader的要求,这个Reader就是一个接口,接口就是一个标准,一个要求,一个规定,这个规定就是“要实现接口中的方法”。只要某个对象符合Reader接口的要求,那么这个对象就可以当作Reader接口来使用,就可以传递给ReadFull方法。
所以,只要文件对象实现了Read方法,那么ReadFull就可以读取文件中的数据,只要网络对象实现了Read方法,ReadFull就可以读取网络中的数据,只要数据库实现了Read方法,ReadFull就可以读取数据库中的数据,只要磁盘对象实现了Read方法,ReadFull就可以读磁盘中的数据,只要内存对象实现了Read方法,ReadFull就可以读取内存中的数据,只要任何一个对象实现了Read方法,ReadFull就可以读取该对象的数据。
在io包中,定义了许多基本的接口类型,Go语言的标准库中大量使用了这些接口(就像ReadFull一样使用它们),下面我们就来看一看都有哪些接口:
------------------------------------------------------------
//Reader接口包装了基本的Read方法,用于输出自身的数据。
//Read方法用于将对象的数据流读入到p中,返回读取的字节数和遇到的错误。
//在没有遇到读取错误的情况下:
//1、如果读到了数据(n>0),则err应该返回nil。
//2、如果数据被读空,没有数据可读(n==0),则err应该返回EOF。
//如果遇到读取错误,则err应该返回相应的错误信息。
typeReaderinterface{
Read(p[]byte)(nint,errerror)
}
------------------------------
//Writer接口包装了基本的Write方法,用于将数据存入自身。
//Write方法用于将p中的数据写入到对象的数据流中,
//返回写入的字节数和遇到的错误。
//如果p中的数据全部被写入,则err应该返回nil。
//如果p中的数据无法被全部写入,则err应该返回相应的错误信息。
typeWriterinterface{
Write(p[]byte)(nint,errerror)
}
------------------------------
//Closer接口包装了基本的Close方法,用于关闭数据读写。
//Close一般用于关闭文件,关闭通道,关闭连接,关闭数据库等
typeCloserinterface{
Close()error
}
------------------------------
//Seeker接口包装了基本的Seek方法,用于移动数据的读写指针。
//Seek设置下一次读写操作的指针位置,每次的读写操作都是从指针位置开始的。
//whence的含义:
//如果whence为0:表示从数据的开头开始移动指针。
//如果whence为1:表示从数据的当前指针位置开始移动指针。
//如果whence为2:表示从数据的尾部开始移动指针。
//offset是指针移动的偏移量。
//返回新指针位置和遇到的错误。
typeSeekerinterface{
Seek(offsetint64,whenceint)(retint64,errerror)
}
------------------------------
//下面是这些接口的组合接口
typeReadWriterinterface{
Reader
Writer
}
typeReadSeekerinterface{
Reader
Seeker
}
typeWriteSeekerinterface{
Writer
Seeker
}
typeReadWriteSeekerinterface{
Reader
Writer
Seeker
}
typeReadCloserinterface{
Reader
Closer
}
typeWriteCloserinterface{
Writer
Closer
}
typeReadWriteCloserinterface{
Reader
Writer
Closer
}
------------------------------
//ReaderFrom接口包装了基本的ReadFrom方法,用于从r中读取数据存入自身。
//直到遇到EOF或读取出错为止,返回读取的字节数和遇到的错误。
typeReaderFrominterface{
ReadFrom(rReader)(nint64,errerror)
}
------------------------------
//WriterTo接口包装了基本的WriteTo方法,用于将自身的数据写入w中。
//直到数据全部写入完毕或遇到错误为止,返回写入的字节数和遇到的错误。
typeWriterTointerface{
WriteTo(wWriter)(nint64,errerror)
}
------------------------------
//ReaderAt接口包装了基本的ReadAt方法,用于将自身的数据写入p中。
//ReadAt忽略之前的读写位置,从起始位置的off偏移处开始读取。
//
//返回写入的字节数和遇到的错误,如果p被写满,则err会返回nil。如果p没
//有被写满,则会返回一个错误信息用于说明为什么没有写满(比如io.EOF)。在这
//方面ReadAt比Read更严格。如果p被写满的同时,自身的数据也刚好被读完,
//则err即可以返回nil也可以返回io.EOF。
//
//即使不能将p填满,ReadAt在被调用时也可能会使用整个p的空间作为缓存空间。
//如果ReadAt自身的数据是从其它地方(比如网络)获取数的,那么在写入p的时
//候,如果没有把p写满(比如网络延时),则ReadAt会阻塞,直到获取更多的数
//据把p写满,或者所有数据都获取完毕,或者遇到读取错误(比如超时)时才返回。
//在这方面,ReadAt和Read是不同的。
//
//如果ReadAt读取的对象是某个有偏移量的底层数据流时,则ReadAt方法既不能影
//响底层的偏移量,也不应该被底层的偏移量影响。
//
//ReadAt的调用者可以对同一数据流并行执行ReadAt方法。
//
//ReaderAt的实现者不应该持有p。
typeReaderAtinterface{
ReadAt(p[]byte,offint64)(nint,errerror)
}
------------------------------
//WriterAt接口包装了基本的WriteAt方法,用于将p中的数据写入自身。
//ReadAt忽略之前的读写位置,从起始位置的off偏移处开始写入。
//
//返回写入的字节数和遇到的错误。如果p没有被读完,则必须返回一个err值来说
//明为什么没有读完。
//
//如果WriterAt写入的对象是某个有偏移量的底层数据流时,则ReadAt方法既不能
//影响底层的偏移量,也不应该被底层的偏移量影响。
//
//WriterAt的调用者可以对同一数据流的不同区段并行执行WriteAt方法。
//
//WriterAt的实现者不应该持有p。
typeWriterAtinterface{
WriteAt(p[]byte,offint64)(nint,errerror)
}
------------------------------
//ByteReader接口包装了基本的ReadByte方法,用于从自身读出一个字节。
//返回读出的字节和遇到的错误。
typeByteReaderinterface{
ReadByte()(cbyte,errerror)
}
------------------------------
//ByteScanner在ByteReader的基础上增加了一个UnreadByte方法,用于撤消最后
//一次的ReadByte操作,以便下次的ReadByte操作可以读出与前一次一样的数据。
//UnreadByte之前必须是ReadByte才能撤消成功,否则可能会返回一个错误信息(根
//据不同的需求,UnreadByte也可能返回nil,允许随意调用UnreadByte,但只有最
//后一次的ReadByte可以被撤销,其它UnreadByte不执行任何操作)。
typeByteScannerinterface{
ByteReader
UnreadByte()error
}
------------------------------
//ByteWriter接口包装了基本的WriteByte方法,用于将一个字节写入自身
//返回遇到的错误
typeByteWriterinterface{
WriteByte(cbyte)error
}
------------------------------
//RuneReader接口包装了基本的ReadRune方法,用于从自身读取一个UTF-8编码的
//字符到r中。
//返回读取的字符、字符的编码长度和遇到的错误。
typeRuneReaderinterface{
ReadRune()(rrune,sizeint,errerror)
}
------------------------------
//RuneScanner在RuneReader的基础上增加了一个UnreadRune方法,用于撤消最后
//一次的ReadRune操作,以便下次的ReadRune操作可以读出与前一次一样的数据。
//UnreadRune之前必须是ReadRune才能撤消成功,否则可能会返回一个错误信息(根
//据不同的需求,UnreadRune也可能返回nil,允许随意调用UnreadRune,但只有最
//后一次的ReadRune可以被撤销,其它UnreadRune不执行任何操作)。
typeRuneScannerinterface{
RuneReader
UnreadRune()error
}
------------------------------
//bytes.NewBuffer实现了很多基本的接口,可以通过bytes包学习接口的实现
funcmain(){
buf:=bytes.NewBuffer([]byte("HelloWorld!"))
b:=make([]byte,buf.Len())
n,err:=buf.Read(b)
fmt.Printf("%s%v\n",b[:n],err)
//HelloWorld!<nil>
buf.WriteString("ABCDEFG\n")
buf.WriteTo(os.Stdout)
//ABCDEFG
n,err=buf.Write(b)
fmt.Printf("%d%s%v\n",n,buf.String(),err)
//12HelloWorld!<nil>
c,err:=buf.ReadByte()
fmt.Printf("%c%s%v\n",c,buf.String(),err)
//HelloWorld!<nil>
c,err=buf.ReadByte()
fmt.Printf("%c%s%v\n",c,buf.String(),err)
//elloWorld!<nil>
err=buf.UnreadByte()
fmt.Printf("%s%v\n",buf.String(),err)
//elloWorld!<nil>
err=buf.UnreadByte()
fmt.Printf("%s%v\n",buf.String(),err)
//elloWorld!bytes.Buffer:UnreadByte:previousoperationwasnotaread
}
------------------------------------------------------------
//WriteString将字符串s写入到w中,返回写入的字节数和遇到的错误。
//如果w实现了WriteString方法,则优先使用该方法将s写入w中。
//否则,将s转换为[]byte,然后调用w.Write方法将数据写入w中。
funcWriteString(wWriter,sstring)(nint,errerror)
//ReadAtLeast从r中读取数据到buf中,要求至少读取min个字节。
//返回读取的字节数和遇到的错误。
//如果min超出了buf的容量,则err返回io.ErrShortBuffer,否则:
//1、读出的数据长度==0,则err返回EOF。
//2、读出的数据长度<min,则err返回io.ErrUnexpectedEOF。
//3、读出的数据长度>=min,则err返回nil。
funcReadAtLeast(rReader,buf[]byte,minint)(nint,errerror)
//ReadFull的功能和ReadAtLeast一样,只不过min=len(buf)
funcReadFull(rReader,buf[]byte)(nint,errerror)
//CopyN从src中复制n个字节的数据到dst中,返回复制的字节数和遇到的错误。
//只有当written=n时,err才返回nil。
//如果dst实现了ReadFrom方法,则优先调用该方法执行复制操作。
funcCopyN(dstWriter,srcReader,nint64)(writtenint64,errerror)
//Copy从src中复制数据到dst中,直到所有数据都复制完毕,返回复制的字节数和
//遇到的错误。如果复制过程成功结束,则err返回nil,而不是EOF,因为Copy的
//定义为“直到所有数据都复制完毕”,所以不会将EOF视为错误返回。
//如果src实现了WriteTo方法,则调用src.WriteTo(dst)复制数据,否则
//如果dst实现了ReadeFrom方法,则调用dst.ReadeFrom(src)复制数据
funcCopy(dstWriter,srcReader)(writtenint64,errerror)
//CopyBuffer相当于Copy,只不Copy在执行的过程中会创建一个临时的缓冲区来中
//转数据,而CopyBuffer则可以单独提供一个缓冲区,让多个复制操作共用同一个缓
//冲区,避免每次复制操作都创建新的缓冲区。如果buf==nil,则CopyBuffer会
//自动创建缓冲区。
funcCopyBuffer(dstWriter,srcReader,buf[]byte)(writtenint64,errerror)
------------------------------
//示例:WriteString、ReadAtLeast、ReadFull
funcmain(){
io.WriteString(os.Stdout,"HelloWorld!\n")
//HelloWorld!
r:=strings.NewReader("HelloWorld!")
b:=make([]byte,15)
n,err:=io.ReadAtLeast(r,b,20)
fmt.Printf("%q%d%v\n",b[:n],n,err)
//""0shortbuffer
r.Seek(0,0)
b=make([]byte,15)
n,err=io.ReadFull(r,b)
fmt.Printf("%q%d%v\n",b[:n],n,err)
//"HelloWorld!"12unexpectedEOF
}
------------------------------
//示例:CopyN、Copy、CopyBuffer
funcmain(){
r:=strings.NewReader("HelloWorld!")
buf:=make([]byte,32)
n,err:=io.CopyN(os.Stdout,r,5)//Hello
fmt.Printf("\n%d%v\n\n",n,err)//5<nil>
r.Seek(0,0)
n,err=io.Copy(os.Stdout,r)//HelloWorld!
fmt.Printf("\n%d%v\n\n",n,err)//12<nil>
r.Seek(0,0)
r2:=strings.NewReader("ABCDEFG")
r3:=strings.NewReader("abcdefg")
n,err=io.CopyBuffer(os.Stdout,r,buf)//HelloWorld!
fmt.Printf("\n%d%v\n",n,err)//12<nil>
n,err=io.CopyBuffer(os.Stdout,r2,buf)//ABCDEFG
fmt.Printf("\n%d%v\n",n,err)//7<nil>
n,err=io.CopyBuffer(os.Stdout,r3,buf)//abcdefg
fmt.Printf("\n%d%v\n",n,err)//7<nil>
}
------------------------------------------------------------
//LimitReader对r进行封装,使其最多只能读取n个字节的数据。相当于对r做了
//一个切片r[:n]返回。底层实现是一个*LimitedReader(只有一个Read方法)。
funcLimitReader(rReader,nint64)Reader
//MultiReader将多个Reader封装成一个单独的Reader,多个Reader会按顺序读
//取,当多个Reader都返回EOF之后,单独的Reader才返回EOF,否则返回读取
//过程中遇到的任何错误。
funcMultiReader(readers...Reader)Reader
//MultiReader将向自身写入的数据同步写入到所有writers中。
funcMultiWriter(writers...Writer)Writer
//TeeReader对r进行封装,使r在读取数据的同时,自动向w中写入数据。
//它是一个无缓冲的Reader,所以对w的写入操作必须在r的Read操作结束
//之前完成。所有写入时遇到的错误都会被作为Read方法的err返回。
funcTeeReader(rReader,wWriter)Reader
------------------------------
//示例LimitReader
funcmain(){
r:=strings.NewReader("HelloWorld!")
lr:=io.LimitReader(r,5)
n,err:=io.Copy(os.Stdout,lr)//Hello
fmt.Printf("\n%d%v\n",n,err)//5<nil>
}
------------------------------
//示例MultiReader
funcmain(){
r1:=strings.NewReader("HelloWorld!")
r2:=strings.NewReader("ABCDEFG")
r3:=strings.NewReader("abcdefg")
b:=make([]byte,15)
mr:=io.MultiReader(r1,r2,r3)
forn,err:=0,error(nil);err==nil;{
n,err=mr.Read(b)
fmt.Printf("%q\n",b[:n])
}
//"HelloWorld!"
//"ABCDEFG"
//"abcdefg"
//""
r1.Seek(0,0)
r2.Seek(0,0)
r3.Seek(0,0)
mr=io.MultiReader(r1,r2,r3)
io.Copy(os.Stdout,mr)
//HelloWorld!ABCDEFGabcdefg
}
------------------------------
//示例MultiWriter
funcmain(){
r:=strings.NewReader("HelloWorld!\n")
mw:=io.MultiWriter(os.Stdout,os.Stdout,os.Stdout)
r.WriteTo(mw)
//HelloWorld!
//HelloWorld!
//HelloWorld!
}
//示例TeeReader
funcmain(){
r:=strings.NewReader("HelloWorld!")
b:=make([]byte,15)
tr:=io.TeeReader(r,os.Stdout)
n,err:=tr.Read(b)//HelloWorld!
fmt.Printf("\n%s%v\n",b[:n],err)//HelloWorld!<nil>
}
------------------------------------------------------------
//NewSectionReader对r进行封装,使其只能从off位置开始读取,最多只能读取n
//个字节的的数据。相当于对r做了一个切片r[off:off+n]返回。
//底层实现是一个*SectionReader。
funcNewSectionReader(rReaderAt,offint64,nint64)*SectionReader
//SectionReader实现了如下接口:
//io.Reader
//io.ReaderAt
//io.Seeker
//Size返回允许读取部分的大小(即切片的长度n)
func(s*SectionReader)Size()
------------------------------
//示例SectionReader
funcmain(){
r:=strings.NewReader("HelloWorld!")
sr:=io.NewSectionReader(r,6,5)
n,err:=io.Copy(os.Stdout,sr)//World
fmt.Printf("\n%d%d%v\n",sr.Size(),n,err)//55<nil>
}
------------------------------------------------------------
//Pipe在内存中创建一个同步管道,用于不同区域的代码之间相互传递数据。
//返回的*PipeReader用于从管道中读取数据,*PipeWriter用于向管道中写入数据。
//管道没有缓冲区,读写操作可能会被阻塞。可以安全的对管道进行并行的读、写或关闭
//操作,读写操作会依次执行,Close会在被阻塞的I/O操作结束之后完成。
funcPipe()(*PipeReader,*PipeWriter)
//从管道中读取数据,如果管道被关闭,则会返会一个错误信息:
//1、如果写入端通过CloseWithError方法关闭了管道,则返回关闭时传入的错误信息。
//2、如果写入端通过Close方法关闭了管道,则返回io.EOF。
//3、如果是读取端关闭了管道,则返回io.ErrClosedPipe。
func(r*PipeReader)Read(data[]byte)(nint,errerror)
//关闭管道
func(r*PipeReader)Close()error
//关闭管道并传入错误信息。
func(r*PipeReader)CloseWithError(errerror)error
//向管道中写入数据,如果管道被关闭,则会返会一个错误信息:
//1、如果读取端通过CloseWithError方法关闭了管道,则返回关闭时传入的错误信息。
//2、如果读取端通过Close方法关闭了管道,则返回io.ErrClosedPipe。
//3、如果是写入端关闭了管道,则返回io.ErrClosedPipe。
func(w*PipeWriter)Write(data[]byte)(nint,errerror)
//关闭管道
func(w*PipeWriter)Close()error
//关闭管道并传入错误信息。
func(w*PipeWriter)CloseWithError(errerror)error
------------------------------
//示例:管道(读取端关闭)
funcmain(){
r,w:=io.Pipe()
//启用一个例程进行读取
gofunc(){
buf:=make([]byte,5)
forn,err:=0,error(nil);err==nil;{
n,err=r.Read(buf)
r.CloseWithError(errors.New("管道被读取端关闭"))
fmt.Printf("读取:%d,%v,%s\n",n,err,buf[:n])
}
}()
//主例程进行写入
n,err:=w.Write([]byte("HelloWorld!"))
fmt.Printf("写入:%d,%v\n",n,err)
}
------------------------------
//示例:管道(写入端关闭)
funcmain(){
r,w:=io.Pipe()
//启用一个例程进行读取
gofunc(){
buf:=make([]byte,5)
forn,err:=0,error(nil);err==nil;{
n,err=r.Read(buf)
fmt.Printf("读取:%d,%v,%s\n",n,err,buf[:n])
}
}()
//主例程进行写入
n,err:=w.Write([]byte("HelloWorld!"))
fmt.Printf("写入:%d,%v\n",n,err)
w.CloseWithError(errors.New("管道被写入端关闭"))
n,err=w.Write([]byte("HelloWorld!"))
fmt.Printf("写入:%d,%v\n",n,err)
time.Sleep(time.Second*1)
}
------------------------------------------------------------
本文内容总结:
原文链接:https://www.cnblogs.com/golove/p/3276678.html