golang ioutil与io包详解

golang ioutil与io包详解


前提概要,公共错误处理

func checkErr(err error) {
	if err != nil {
		log.Fatil(err)
	}
}

输出文件目录
在这里插入图片描述
file1.txt中的内容为1行 1:街角魔族是最好看的动漫!
file2.txt中的内容为2行 2:街角魔族是最好看的动漫!
还有一个输入文件夹in,不过是空的就不截图了,到时候底下的例子凡是关于write的都是放进in文件夹里。

ioutil

首先就从最简单的ioutil开始把。
先申明,ioutil的所有方法我仅举例子说明方法的用处,至于返回值或函数传参,凡是关于io属性与os属性的详细用法请自行往下查找io相关内容或os相关博文

1、NopCloser(r io.Reader) io.ReadCloser用一个无操作的Close方法包装r返回一个ReadCloser接口。

	//打开文件
	file, err := os.Open("./out/file1.txt")
	checkErr(err)
	//通过io.Reader获得一个io.ReadCloser
	readCloser := ioutil.NopCloser(file)

注意这里的Close没有实际操作,源码如下⬇

func (nopCloser) Close() error { return nil }

2、ReadAll(r io.Reader) ([]byte, error)从r读取数据直到EOF或遇到error,返回读取的数据和可能的错误。成功的调用返回的err为nil而非EOF。因为本函数定义为读取r直到EOF,它不会将读取返回的EOF视为应报告的错误。

file, err := os.Open("./out/file1.txt")
checkErr(err)
fileBytes, err := ioutil.ReadAll(file)
checkErr(err)
fmt.Println(string(fileBytes))

正确的输出:

1:街角魔族是最好看的动漫!

3、ReadFile(filename string) ([]byte, error)从filename指定的文件中读取数据并返回文件的内容。对err的判断和ReadAll一样。

fileBytes, err := ioutil.ReadFile("./out/file2.txt")
checkErr(err)
fmt.Println(string(fileBytes))

输出

2:街角魔族是最好看的动漫!
2:街角魔族是最好看的动漫!

4、WriteFile(filename string, data []byte, perm os.FileMode) error函数向filename指定的文件中写入数据。如果文件不存在将按给出的perm权限创建文件,否则在写入数据之前清空文件。

	fileBytes, err := ioutil.ReadFile("./out/街角魔族1.jpg")
	checkErr(err)
	//以全权限写文件
	ioutil.WriteFile("./in/街角魔族1.jpg", fileBytes, 0777)

输出效果,没有字节损失,依旧如此高清可爱,完美。
街角魔族1
5、ReadDir(dirname string) ([]os.FileInfo, error)读取dirname目录内的所有文件信息,注意此序列有序。

	osFiles, err := ioutil.ReadDir("./out")
	if err != nil {
		println(err)
		return
	}
	for i, osFile := range osFiles {
		//打印下标和文件名
		fmt.Println(i, osFile.Name())
	}

输出:

0 file1.txt
1 file2.txt
2 理直气壮.jpg
3 街角魔族1.jpg
4 街角魔族2.png

6、TempDir(dir, prefix string) (name string, err error)在dir目录里创建一个新的、使用prfix作为前缀的临时文件夹,并返回文件夹的路径。如果dir是空字符串,TempDir使用默认用于临时文件的目录(参见os.TempDir函数)。 不同程序同时调用该函数会创建不同的临时目录,调用本函数的程序有责任不需要临时文件夹时摧毁它。

	name, err := ioutil.TempDir("./in", "temp")
	checkErr(err)
	fmt.Println("已创建文件夹,目录:" + name)
	//等待1秒后删除创建的临时文件夹
	time.Sleep(time.Second)
	err = os.Remove(name)
	checkErr(err)
	fmt.Println("已删除文件夹,目录:" + name)

输出:

已创建文件夹,目录:in\temp103398839
已删除文件夹,目录:in\temp103398839

7、TempFile(dir, prefix string) (f *os.File, err error)在dir目录下创建一个新的、使用prefix为前缀的临时文件,以读写模式打开该文件并返回os.File指针。如果dir是空字符串,TempFile使用默认用于临时文件的目录(参见os.TempDir函数)。责任与TempDir相同。

//在./in目录中创建一个前缀为temp,后缀为.png的临时文件
filePoint, err := ioutil.TempFile("./in", "temp*.png")
checkErr(err)
fmt.Println("已创建临时文件:目录", filePoint.Name())
fileBytes, err := ioutil.ReadFile("./out/街角魔族2.png")
checkErr(err)
_, err = filePoint.Write(fileBytes)
//关闭文件流,不关将无法删除。
filePoint.Close()
//等待10秒后删除创建的临时文件夹,文件留10秒截个效果图
time.Sleep(10 * time.Second)
err = os.Remove(filePoint.Name())
checkErr(err)
fmt.Println("已删除临时文件,目录", filePoint.Name())

结果非常perfect,高清可爱。
街角魔族2

已创建临时文件:目录 in\temp224024071.png
已删除临时文件,目录 in\temp224024071.png

好了到这里outil的所有内容都已经写完了,有没有发现ioutil的方法往往只需要一句,并不需要其他语句就能完成功能,非常简洁明了。毕竟ioutil包的创建初衷就只是对io包实用功能的二次封装而已,同样的由于高度的封装,是不是对里面很多参数都很懵逼呢,如:perm os.FileMode,io.ReadCloser,io.Reader这种,感兴趣的话就接着看吧。

io

io包提供了对I/O原语的基本接口。本包的基本任务是包装这些原语已有的实现(如os包里的原语),使之成为共享的公共接口,这些公共接口抽象出了泛用的函数并附加了一些相关的原语的操作,非并发安全包。
原语:计算机进程的控制通常由原语完成。所谓原语,一般是指由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被中断。

错误变量

首先来看看包中的错误变量
var EOF = errors.New(“EOF”)
正常输入结束Read返回EOF,如果在一个结构化数据流中EOF在不期望的位置出现了,则应返回错误ErrUnexpectedEOF或者其它给出更多细节的错误。

var ErrClosedPipe = errors.New(“io: read/write on closed pipe”)
当从一个已关闭的Pipe读取或者写入时,会返回ErrClosedPipe。

var ErrNoProgress = errors.New(“multiple Read calls return no data or error”)
某些使用io.Reader接口的客户端如果多次调用Read都不返回数据也不返回错误时,就会返回本错误,一般来说是io.Reader的实现有问题的标志。

var ErrShortBuffer = errors.New(“short buffer”)
ErrShortBuffer表示读取操作需要大缓冲,但提供的缓冲不够大。

var ErrShortWrite = errors.New(“short write”)
ErrShortWrite表示写入操作写入的数据比提供的少,却没有显式的返回错误。

var ErrUnexpectedEOF = errors.New(“unexpected EOF”)
ErrUnexpectedEOF表示在读取一个固定尺寸的块或者数据结构时,在读取未完全时遇到了EOF。

接口

1、

type Reader interface {
    Read(p []byte) (n int, err error)
}

一个Reader接口,只要是实现了Read方法的结构体都代表实现了Reader接口,os的很多关于file的结构体都是实现了Reader接口。想要实现Reader接口非常简单,只要实现了Read方法即可。
Read方法读取len§字节数据写入p。它返回写入的字节数和遇到的任何错误。
很多时候我们也可以通过自己实现Reader接口来给需要io.Reader
传参的函数使用,举个例子。

func main(){
		//实例化一个实现了io.Reader接口的结构体
	rs := ReaderStruct{"街角魔族是最好看的动漫!"}
	//实例化一个text长度的[]byte
	buf := make([]byte, len(rs.text))
	//将rs读取至buf,返回长度
	n, err := io.ReadFull(rs, buf)
	checkErr(err)
	fmt.Println(string(buf))
	fmt.Println(n)
}

type ReaderStruct struct {
	text string
}
//实现Reader接口中的Read方法,总的功能就是将ReaderStruct中的text属性读取出来
func (rs ReaderStruct) Read(p []byte) (n int, err error) {
	bf := bytes.NewBufferString(rs.text)
	bytes := bf.Bytes()
	i := 0
	for i < len(p) {
		p[i] = bytes[i]
		i++
	}
	n = len(p)
	err = io.EOF
	return n, err
}

输出

街角魔族是最好看的动漫!
34

是不是很简单呢,对于字符操作有不懂的可以看这篇文章

2、

type Writer interface {
    Write(p []byte) (n int, err error)
}

Writer接口用于包装基本的写入方法。
Write方法len( p )字节数据从p写入底层的数据流。它会返回写入的字节数(0 <= n <= len( p ))和遇到的任何导致写入提取结束的错误。Write必须返回非nil的错误,如果它返回的 n < len( p )。Write不能修改切片p中的数据,即使临时修改也不行。

func main(){
	//此处的buf为上一个例子Reader代码中实例化并赋值的
	buffer := bytes.NewBuffer(buf)
	ws := WriterStruct{}
	//将buffer中的buf写入ws
	buffer.WriteTo(&ws)
	fmt.Println("writerStruct.bytes:", string(ws.bytes))
}	
type WriterStruct struct {
	bytes []byte
}

func (ws *WriterStruct) Write(p []byte) (int, error) {
	ws.bytes = append(ws.bytes, p...)
	return len(p), nil
}

这个例子要和上一个例子结合着看,里面的buf在上一个例子中有读ReaderStruct里的text值并赋值至buf。
输出:

writerStruct.bytes: 街角魔族是最好看的动漫!

3、

type Closer interface {
    Close() error
}

Closer接口用于包装基本的关闭方法。
在第一次调用之后再次被调用时,Close方法的的行为是未定义的。某些实现可能会说明他们自己的行为。
在整个标准库内都没有对Closer的引用,只有实现,因此这个就不举例了,用法都是开启某某连接/流,在用完/报错后在进行Close的操作。

4、

type Seeker interface {
    Seek(offset int64, whence int) (int64, error)
}

Seeker接口用于包装基本的移位方法。
Seek方法设定下一次读写的位置:偏移量为offset,校准点由whence确定:0表示相对于文件起始;1表示相对于当前位置;2表示相对于文件结尾。Seek方法返回新的位置以及可能遇到的错误。
移动到一个绝对偏移量为负数的位置会导致错误。移动到任何偏移量为正数的位置都是合法的,但其下一次I/O操作的具体行为则要看底层的实现。
在整个标准库内都没有对Seeker的引用,只有实现,因此这个就不举例了。

5、

type ReadCloser interface {
    Reader
    Closer
}

见名知意,就是Reader+Closer,例如在ioutil中的NopCloser方法返回的就是一个ReadCloser,但是里面的Close就是个空函数,毫无作用。

6、

type ReadSeeker interface {
    Reader
    Seeker
}

ReadSeeker接口聚合了基本的读取和移位操作。

7、

type WriteCloser interface {
    Writer
    Closer
}

WriteCloser接口聚合了基本的写入和关闭操作。

8、

type WriteSeeker interface {
    Writer
    Seeker
}

WriteSeeker接口聚合了基本的写入和移位操作。

9、

type ReadWriter interface {
    Reader
    Writer
}

ReadWriter接口聚合了基本的读写操作。

10、

type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

ReadWriteCloser接口聚合了基本的读写和关闭操作。

11、

type ReadWriteSeeker interface {
    Reader
    Writer
    Seeker
}

ReadWriteSeeker接口聚合了基本的读写和移位操作

12、

type ReaderAt interface {
    ReadAt(p []byte, off int64) (n int, err error)
}

ReaderAt接口包装了的ReadAt方法。

ReadAt从底层输入流的偏移量off位置读取len( p )字节数据写入p, 它返回读取的字节数(0 <= n <= len( p ))和遇到的任何错误。

当ReadAt返回n<len( p )时,它返回一个非零错误,解释为什么没有返回更多的字节。在这方面,Readat比Read更严格。

即使ReadAt方法返回值 n < len( p ),它在被调用时仍可能使用p的全部长度作为暂存空间。如果有部分可用数据,但不够len( p )字节,ReadAt会阻塞直到获取len( p )个字节数据或者遇到错误。在这方面,ReadAt和Read是不同的。

如果ReadAt返回的n=len( p )字节位于输入源的末尾,那么ReadAt可以返回err== EOF或err ==nil。

如果ReadAt是从某个有偏移量的底层输入流(的Reader包装)读取,ReadAt方法既不应影响底层的偏移量,也不应被底层的偏移量影响。

ReadAt的调用者可以对同一个输入源执行并行ReadAt调用。实现不能保留p。

13、

type WriterAt interface {
    WriteAt(p []byte, off int64) (n int, err error)
}

WriterAt接口包装了基本的WriteAt方法。

在偏移结束时,WriteAt将len§字节从p写入基础数据流。它返回从p(0<=n<=len§)写入的字节数以及导致写入提前停止的任何错误。如果写入错误返回n<len§,则必须返回非零错误。

如果WriteAt使用查找偏移量写入目标,则WriteAt不应影响也不受基础查找偏移量的影响。

如果写入范围不重叠,WriteAt的调用方可以在同一目标上执行并行WriteAt调用。实现不能保留p。

14、

type ByteReader interface {
    ReadByte() (c byte, err error)
}

ByteReader是基本的ReadByte方法的包装。

ReadByte读取输入中的单个字节并返回。如果没有字节可读取,会返回错误。

15、

type ByteReader interface {
    ReadByte() (c byte, err error)
}

ByteReader是基本的ReadByte方法的包装。

ReadByte读取输入中的单个字节并返回。如果没有字节可读取,会返回错误。

16、

type ByteScanner interface {
    ByteReader
    UnreadByte() error
}

ByteScanner接口在基本的ReadByte方法之外还添加了UnreadByte方法。

UnreadByte方法让下一次调用ReadByte时返回之前调用ReadByte时返回的同一个字节。连续调用两次UnreadByte方法而中间没有调用ReadByte时,可能会导致错误。

17、

type ByteWriter interface {
    WriteByte(c byte) error
}

ByteWriter是包装WriteByte方法的接口。

18、

type RuneReader interface {
    ReadRune() (r rune, size int, err error)
}

RuneReader是基本的ReadRune方法的包装。

ReadRune读取单个utf-8编码的字符,返回该字符和它的字节长度。如果没有有效的字符,会返回错误。

19、

type RuneScanner interface {
    RuneReader
    UnreadRune() error
}

RuneScanner接口在基本的ReadRune方法之外还添加了UnreadRune方法。

UnreadRune方法让下一次调用ReadRune时返回之前调用ReadRune时返回的同一个utf-8字符。连续调用两次UnreadRune方法而中间没有调用ReadRune时,可能会导致错误。

20、

type ReaderFrom interface {
    ReadFrom(r Reader) (n int64, err error)
}

ReaderFrom是包装ReadFrom方法的接口。

ReadFrom方法从r读取数据直到EOF或者遇到错误。返回值n是读取的字节数,执行时遇到的错误(EOF除外)也会被返回。

21、

type WriterTo interface {
    WriteTo(w Writer) (n int64, err error)
}

WriterTo是包装WriteTo方法的接口。

WriterTo写入数据到w,直到没有更多的数据要写入或发生错误时。返回值n是写入的字节数。还将返回写入过程中遇到的任何错误。如果可用,复制函数将使用WriterTo。

以上就是io包全部的21个公共接口了。

结构体

1、

type LimitedReader struct {
    R   Reader // underlying reader
    N   int64  // max bytes remaining
}

LimitedReader从R读取,但将返回的数据量限制为N个字节。每次读取更新N以标记剩余可以读取的字节数。Read在N<=0时或基础R返回EOF时返回EOF。
具体实现方法有:
1)func (l *LimitedReader) Read(p []byte) (n int, err error)
Reader接口的实现方法

r := strings.NewReader("街角魔族是最好看的动漫!")
//限制27字节,只读取9字
n := int64(27)
limitReader := io.LimitReader(r, n)
bs := make([]byte, n)
_, err := lr.Read(bs)
checkErr(err)
fmt.Println(string(bs))

输出:

街角魔族是最好看的

2、

type PipeReader struct {
    // 内含隐藏或非导出字段
}

PipeReader是一个管道的读取端。
具体实现方法有:
1)func (r *PipeReader) Read(data []byte) (n int, err error)
Read实现了标准的读取接口:它从管道中读取数据,阻塞直到写入端到达或写入端被关闭。如果用错误关闭写入端,则返回错误为ERR;否则ERR为EOF。

2)func (r *PipeReader) Close() error
Close关闭读取器;关闭后如果对管道的写入端进行写入操作,就会返回(0, ErrClosedPip)。

3)func (r *PipeReader) CloseWithError(err error) error
CloseWithError类似Close方法,但将调用Write时返回的错误改为err。

3、

type PipeWriter struct {
    // 内含隐藏或非导出字段
}

PipeWriter是一个管道的写入端。
具体实现方法有:
1)func (w *PipeWriter) Write(data []byte) (n int, err error)
Write实现了标准的写接口:它将数据写入管道,直到一个或多个读取端消耗完所有数据或读取端关闭为止。如果以错误关闭读取端,则该错误将作为ERR返回;否则ERR将为ErrClosedPipe。

2)func (w *PipeWriter) Close() error
Close关闭写入器;关闭后如果对管道的读取端进行读取操作,就会返回(0, EOF)。

3)func (w *PipeWriter) CloseWithError(err error) error
CloseWithError类似Close方法,但将调用Read时返回的错误改为err。

注:以上两个结构体PipeWriter与PipeReader是结合使用的需要用Pipe()方法进行创建。

创建函数func Pipe() (*PipeReader, *PipeWriter)
Pipe创建一个同步的内存管道。
可用于连接期望io.Reader的代码和期望io.Writer的代码。

管道上的读和写是一对一匹配的,除非需要多次读取才能使用单次写入。也就是说,每次对PipeWriter的写入都将阻塞,直到它满足从PipeReader读取的一个或多个读取,这些读取会完全消耗已写入的数据。

数据直接从Write复制到相应的Read (或Reads);没有内部缓冲。
对读的并行调用和对写的并行调用也是安全的:单个调用将按顺序执行。
举个例子⬇

	r, w := io.Pipe()
	//开启写协程
	go func() {
		for i := 0; i < 3; i++ {
			buf := bytes.NewBufferString("街角魔族是最好看的动漫!\n")
			w.Write(buf.Bytes())
		}
		w.Close()
	}()
	str := strings.Builder{}
	bs := make([]byte, 37)
	//循环读取直至读取结束EOF或其他错误
	for {
		_, err := r.Read(bs)
		if err != nil {
			break
		}
		str.Write(bs)
	}
	r.Close()
	fmt.Println(str.String())

输出:

街角魔族是最好看的动漫!
街角魔族是最好看的动漫!
街角魔族是最好看的动漫!

值得注意的是在没有读取完Writer写入的内容前,主协程main是完全阻塞的,上面的writer如果换一个其他的比如strings.Builder.WriteString()连协程都进不去,主协程就执行完毕了。

4、

type SectionReader struct {
    // contains filtered or unexported fields
}

SectionReader在ReaderAt的基础上实现了Read,Seek和ReadAt。
具体实现方法有:

1)func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader
结构体SectionReader的创建方法
NewSectionReader返回一个SectionReader,它从r开始读取,偏移量为off,并在n个字节后以EOF停止。

2)func (s *SectionReader) Read(p []byte) (n int, err error)
实现了接口Reader的Read方法

3)func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error)
实现了接口ReaderAt的ReadAt方法

4)func (s *SectionReader) Seek(offset int64, whence int) (int64, error)
实现了接口Seeker的Seek方法
func (*SectionReader) Size

5)func (s *SectionReader) Size() int64
Size返回以字节为单位的片段大小。
举个例子⬇

	//SectionReader Method NewReader
	r := strings.NewReader("真言:街角魔族是最好看的动漫!")
	s := io.NewSectionReader(r, 9, 33)
	fmt.Println("原Reader内容:", *r)
	fmt.Printf("创建SectionReader,跳过%d个字节读取%d个字节\n", 9, 33)
	ReadBytes := make([]byte, r.Len())
	//SectionReader Method Read
	s.Read(ReadBytes)
	fmt.Println("Read:", string(ReadBytes))
	//SectionReader Method ReadAt
	//s中的内容为 街角魔族是最好看的动漫
	var off int64
	off = 15
	//注意bs的长度不能大于s.Size()-off,此值为最大值,大于将报错
	ReadAtBytes := make([]byte, s.Size()-off)
	//从off的位置开始读取s中的内容存进bs
	n, err := s.ReadAt(ReadAtBytes, off)
	checkErr(err)
	fmt.Println("ReadAt:", string(ReadAtBytes), n)
	//SectionReader Method seek
	//在下一次的读写操作中设置相对于whence的偏移量offset
	//返回相对于文件开头的新偏移量n与可能的错误
	//具体请看文章type Seeker的描述
	n64, err := s.Seek(6, io.SeekStart)
	checkErr(err)
	fmt.Printf("从%d开始读6字节\n", n64)
	buf := make([]byte, 6)
	_, err = s.Read(buf)
	checkErr(err)
	fmt.Println("经过seek的Read", string(buf), n64)
	//注意这里的ReadAt是从第3字节开始读的,如果受seek偏移量的影响
	//那么应该是,从 '族'开始,但ReadAt并不受偏移量影响所以从'角'开始读
	_, err = s.ReadAt(buf, 3)
	checkErr(err)
	fmt.Println("经过seek的ReadAt", string(buf))

输出:

原Reader内容: {真言:街角魔族是最好看的动漫! 0 -1}
创建SectionReader,跳过9个字节读取33个字节
Read: 街角魔族是最好看的动漫
ReadAt: 最好看的动漫 186开始读6字节
经过seek的Read 魔族 6
经过seek的ReadAt 角魔

可以看的出来为什么说SectionReader是根据ReaderAt实现的而非Seeker了,虽然两者的效果很像,但是ReaderAt读取内容是无视Seeker偏移量的。且在读取数据大小上ReadAt是要比Read严格的,同样的Bytes在Read上即使设大了也会没事,但在ReadAt会报错。
其实上面说的关于ReadAt的两个注意点在我写的ReaderAt接口的描述中都有说明,但怕你们没注意在举例上实验一遍,还有几个需要注意的点我没举例子实验,你们感兴趣可以往上翻看看ReaderAt接口的描述。
以上就是io包全部的4个公共结构体了。

函数

1、Copy(dst Writer, src Reader) (written int64, err error)
将副本从src复制到dst,直到在src上达到EOF或发生错误。它返回复制的字节数和复制时遇到的第一个错误(如果有)。 成功的复制将返回err == nil而不是err == EOF。因为复制被定义为从src读取直到EOF,所以它不会将读取的EOF视为要报告的错误。 如果src实现WriterTo接口,则通过调用src.WriteTo(dst)实现该副本。否则,如果dst实现了ReaderFrom接口,则通过调用dst.ReadFrom(src)实现该副本

r := strings.NewReader("街角魔族是世界上最好看的动漫!")
w := new(strings.Builder)
//通过r.WriteTo()实现该副本
written, err := io.Copy(w, r)
checkErr(err)
fmt.Println(w.String(), written)

输出:

街角魔族是世界上最好看的动漫! 43

2、func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)
CopyBuffer与Copy相同,区别在于CopyBuffer逐步遍历提供的缓冲区(如果需要),而不是分配临时缓冲区。如果buf为nil,则分配一个;如果长度为零,则CopyBuffer会panic报错。 如果src实现WriterTo或dst实现ReaderFrom,则buf将不用于执行复制。

	r := strings.NewReader("街角魔族是世界上最好看的动漫!")
	w := new(strings.Builder)
	//如果len(buf)==0会报错
	written, err := io.CopyBuffer(w, r, make([]byte, 9))
	checkErr(err)
	fmt.Println(w.String(), written)

输出:

街角魔族是世界上最好看的动漫! 43

3、func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
CopyN将n个字节(或直到出错)从src复制到dst。它返回复制的字节数以及复制时遇到的最早错误。返回时,只有err == nil时,writte == n。 如果dst实现了ReaderFrom接口,则使用该接口实现副本。

	r := strings.NewReader("街角魔族是世界上最好看的动漫!")
	w := new(strings.Builder)
	//如果n大于len(r)会报错
	written, err := io.CopyN(w, r, 36)
	checkErr(err)
	fmt.Println(w.String(), written)

输出:

街角魔族是世界上最好看的 36

4、func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)
ReadAtLeast从r读取到buf,直到它至少读取了min字节。它返回复制的字节数n,如果读取的字节数少则返回错误。仅当未读取任何字节时,错误才是EOF。如果在读取少于最小字节后发生EOF,则ReadAtLeast返回ErrUnexpectedEOF。如果min大于buf的长度,则ReadAtLeast返回ErrShortBuffer。返回时,当且仅当err == nil时,n> = min。

r := strings.NewReader("街角魔族是最好看的动漫!")
//min如果大于len(r)会报错
min := 27
//这边buf的len如果小于min也会报错
buf := make([]byte, min)
n, err := io.ReadAtLeast(r, buf, min)
checkErr(err)
fmt.Println(string(buf), n)

输出:

街角魔族是最好看的 27

5、func ReadFull(r Reader, buf []byte) (n int, err error)
ReadFull将r中的len(buf)个字节准确地读取到buf中。它返回复制的字节数,如果读取的字节数少则返回错误。仅当未读取任何字节时,错误才是EOF。如果在读取了一些但不是全部字节后发生EOF,则ReadFull返回ErrUnexpectedEOF。返回时,当且仅当err == nil时,n == len(buf)。

	r := strings.NewReader("街角魔族是最好看的动漫!")
	buf := make([]byte, 12)
	_, err := io.ReadFull(r, buf)
	checkErr(err)
	fmt.Println(string(buf))
	//buf大于io.Reader时,Err=EOF
	longBuf := make([]byte, 100)
	_, err = io.ReadFull(r, longBuf)
	checkErr(err)

输出:

街角魔族
2020/07/14 16:22:27 unexpected EOF

6、func WriteString(w Writer, s string) (n int, err error)
WriteString将字符串s的内容写入w,w接收一个字节切片。如果w实现了StringWriter,则直接调用其WriteString方法。否则,w.Write只会被调用一次。

io.WriteString(os.Stdout, "街角魔族是世界上最好看的动漫!")

输出:

街角魔族是世界上最好看的动漫!

7、func LimitReader(r Reader, n int64) Reader
LimitReader返回一个从r读取但在n字节后以EOF停止的Reader。 基础实现是一个* LimitedReader。
例子在结构体1写了,很简单就不再重复了。

8、func MultiReader(readers ...Reader) Reader
MultiReader返回一个Reader,它是所提供的输入阅读器的逻辑串联。它们被顺序读取。一旦所有输入均返回EOF,读取将返回EOF。如果任何读取器返回非零,非EOF错误,则Read将返回该错误。

r := strings.NewReader("街角魔族是最好看的动漫!")
r2 := strings.NewReader("说得对")
w := new(strings.Builder)
mr := io.MultiReader(r, r2)
n, err := io.Copy(w, mr)
checkErr(err)
fmt.Println(w.String(), n)

输出:

街角魔族是最好看的动漫!说得对 45

9、func TeeReader(r Reader, w Writer) Reader
TeeReader返回一个Reader,该Reader向w写入从r读取的内容。通过r执行的所有r读取均与对w的相应写入匹配。没有内部缓冲-写入必须在读取完成之前完成。写入时遇到的任何错误均报告为读取错误。

	r := strings.NewReader("街角魔族是最好看的动漫!")
	w := new(strings.Builder)
	tee := io.TeeReader(r, w)

	p := make([]byte, r.Len())
	fmt.Println("读取前w:", w.String())
	_, err := tee.Read(p)
	checkErr(err)
	fmt.Println("读取的值", string(p))
	//所有对tee中的r io.Reader的读取都会往tee中的w io.Writer写入
	fmt.Println("读取后w:", w.String())

输出:

读取前w: 
读取的值 街角魔族是最好看的动漫!
读取后w: 街角魔族是最好看的动漫!

10、func MultiWriter(writers ...Writer) Writer
MultiWriter创建一个Writers,将其写入复制到所有提供的写入器中,类似于Unix tee(1)命令。 每个写入一次写入每个列出的写入器。如果列出的写程序返回错误,则整个写操作将停止并返回错误;它不会在列表中继续下去。

	r := strings.NewReader("街角魔族是世界上最好看的动漫!")
	builder1 := new(strings.Builder)
	builder2 := new(strings.Builder)
	ws := io.MultiWriter(builder1, builder2)
	_, err := io.Copy(ws, r)
	checkErr(err)
	fmt.Print(builder1.String())
	fmt.Print(builder2.String())

输出:

街角魔族是最好看的动漫!街角魔族是最好看的动漫!

以上就是io的全部的10个公共函数了
其实例子早就写完了,但是太忙一直没写博文,本来标题是 ioutil/io/bufio/os io流,文件操作大全,这几个一起用的相关度非常高,但是实在太忙没工夫写os和bufio了,等以后有机会吧。
以上结束。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值