go标准库1-io

go中的IO操作主要由io、io/ioutil、fmt、bufio实现,他们各有自己的特点

  • io 为 IO原语提供基本的接口,他的方法都是异步io,并不保证所有的方法都是并发安全的
  • io/ioutil 主要提供了对文件操作的辅助io和一些常用、方便的IO操作函数
  • bufio 基本的io原语函数调用都需要传入一个缓冲字节,bufio 提供了一个默认的缓冲字节长度,并实现了 io 中常用的接口,可以看做是对原语io操作包装,实现 io 包的接口
  • fmt 提供了格式化的io

并不列举所有的API,只对我频繁用到的做介绍

io

io 包抽象了标准库中所有 io 操作的底层接口和函数,它的很多方法属于底层方法,所以并不全部保证线程安全,go 的 io 操作,都是基于以下的两个接口展开的

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

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

Reader

Read 将 len§ 个字节读取到 p 中。它返回读取的字节数 n(0 <= n <= len§) 以及任何遇到的错误。即使 Read 返回的 n < len§,它也会在调用过程中占用 len§ 个字节作为暂存空间。若可读取的数据不到 len§ 个字节,Read 会返回可用数据,而不是等待更多数据。

当 Read 在成功读取 n > 0 个字节后遇到一个错误或 EOF (end-of-file),它会返回读取的字节数。它可能会同时在本次的调用中返回一个non-nil错误,或在下一次的调用中返回这个错误(且 n 为 0)。 一般情况下, Reader会返回一个非0字节数n, 若 n = len§ 个字节从输入源的结尾处由 Read 返回,Read可能返回 err == EOF 或者 err == nil。并且之后的 Read() 都应该返回 (n:0, err:EOF)。

调用者在考虑错误之前应当首先处理返回的数据。这样做可以正确地处理在读取一些字节后产生的 I/O 错误,同时允许EOF的出现。

io包下,var EOF = errors.New(“EOF”)

看一个简单的例子

//从一个输入源中读取 num 字节
func readNumFrom(r io.Reader,num int) ([]byte,error) {
	b := make([]byte,num)
	n, err := r.Read(b)
	if n > 0{
		return b[:n],nil
	}
	return b,err
}

Writer

Write 将 len§ 个字节从 p 中写入到基本数据流中。它返回从 p 中被写入的字节数 n(0 <= n <= len§)以及任何遇到的引起写入提前停止的错误。若 Write 返回的 n < len§,它就必须返回一个 非nil 的错误。

func println(d ...interface{}) (int,error) {
	return fmt.Fprintln(os.Stdout,d...)
}
//print等方法,底层调用的都是 Fprint

go中io的思想就是,不管你是啥,只要你实现了这个接口,我就能 Write/Read,屏蔽底层的实现细节,很简洁的io流

即使是不同的高层次IO操作,都同样要实现这两个基本接口,他们从根本上就确定是异步缓存的,所以go中的io天生异步

func Pipe() (*PipeReader, *PipeWriter)

提供了同步的 io 操作,它希望 write 后就立刻被 read,否则线程将被阻塞,它是线程安全的,并希望被用于两个以上的线程

它有点像无缓冲的 channel

下面的代码将会堵死

    reader, writer := io.Pipe()
	fmt.Fprintf(writer,"这必须是同步的操作")
	err := writer.Close()
	if err != nil{
		panic(err)
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(reader)
	fmt.Println(buf.String())

必须保持再启动其他的线程同步接收或写入

    reader, writer := io.Pipe()
	go func() {
		fmt.Fprintf(writer,"这必须是同步的操作")
		err := writer.Close()
		if err != nil{
			panic(err)
		}
	}()

	buf := new(bytes.Buffer)
	buf.ReadFrom(reader)
	fmt.Println(buf.String())

MultiReader 和 MultiWriter 函数

func MultiReader(readers …Reader) Reader
func MultiWriter(writers …Writer) Writer

它们接收多个 Reader 或 Writer,返回一个 Reader 或 Writer。我们可以猜想到这两个函数就是操作多个 Reader 或 Writer 就像操作一个

readers := []io.Reader{
	strings.NewReader("string reader"),
	bytes.NewBufferString("[]bytes reader"),
}

multireader := io.MultiReader(readers...)

r := bufio.NewReader(multireader)

buf := new(bytes.Buffer)

buf.ReadFrom(r)
fmt.Println(buf.String())
func multiWriter()  {
	file, err := os.Create("test.txt")
	if err != nil{
		panic(err)
	}
	defer file.Close()

	writers := []io.Writer{
		file,
		os.Stdout,
	}

	mulwri := io.MultiWriter(writers...)
	writer := bufio.NewWriter(mulwri)
	writer.WriteString("康搁搁")
	writer.Flush()
	
}

TeeReader

func TeeReader(r Reader, w Writer) Reader

TeeReader 返回一个 Reader,它将从 r 中读到的数据写入 w 中。所有经由它处理的从 r 的读取都匹配于对应的对 w 的写入。它没有内部缓存,即写入必须在读取完成前完成。任何在写入时遇到的错误都将作为读取错误返回

它不同于 io.copy 的地方是,TeeReader 是流式的数据传输,无缓存

reader := io.TeeReader(os.Stdin, os.Stdout)
reader.Read(make([]byte,200))

猜你喜欢

转载自blog.csdn.net/weixin_39653200/article/details/84670683
今日推荐