首页 文章资讯内容详情

Golang学习 - io 包

2026-06-01 4 花语

本文内容纲要:

------------------------------------------------------------ 先说一下接口,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中 ifa<=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