Go io包

xxggy · · 4112 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Go io包

Go io包的接口概览

io.Writer interface

// Writer 接口包装了基本的 Write 方法,用于将数据存入自身。
// Write 方法用于将 p 中的数据写入到对象的数据流中,
// 返回写入的字节数和遇到的错误。
// 如果 p 中的数据全部被写入,则 err 应该返回 nil。
// 如果 p 中的数据无法被全部写入,则 err 应该返回相应的错误信息。
type Writer interface {
    Write(p []byte) (n int, err error)
}

我们可以通过查看标准库文档,列出实现了io.Reader或io.Writer接口的类型(导出的类型):

os.File 同时实现了io.Reader和io.Writer
strings.Reader 实现了io.Reader
bufio.Reader/Writer 分别实现了io.Reader和io.Writer
bytes.Buffer 同时实现了io.Reader和io.Writer
bytes.Reader 实现了io.Reader
compress/gzip.Reader/Writer 分别实现了io.Reader和io.Writer
crypto/cipher.StreamReader/StreamWriter 分别实现了io.Reader和io.Writer
crypto/tls.Conn 同时实现了io.Reader和io.Writer
encoding/csv.Reader/Writer 分别实现了io.Reader和io.Writer
mime/multipart.Part 实现了io.Reader

以bufio.Writer 为例,来看一下具体的使用方法,

package main

import (
	"bufio"
	"os"
	"fmt"
)

func main() {
	//func Copy(dst Writer, src Reader) (written int64, err error)
	path := "/Users/xinxingegeya/workspace-go/src/awesomeProject/io/hello.txt"
	fd, _ := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0666)

	// 实现一个带缓冲的写文件
	w := bufio.NewWriterSize(fd, 4096)

	fmt.Println(w.Available()) //表示可使用的缓冲区的大小
	fmt.Println(w.Buffered())  //表示已经缓冲的数据的大小

	w.WriteString("hello world!\n")

	w.Write([]byte("hello world!\n"))

	fmt.Println(w.Available()) //表示可使用的缓冲区的大小
	fmt.Println(w.Buffered())  //表示已经缓冲的数据的大小

	//此时需要刷新一下数据才能写入文件
	w.Flush()
}

 

io.Reader interface

// Reader 接口包装了基本的 Read 方法,用于输出自身的数据。
// Read 方法用于将对象的数据流读入到 p 中,返回读取的字节数和遇到的错误。
// 在没有遇到读取错误的情况下:
// 1、如果读到了数据(n > 0),则 err 应该返回 nil。
// 2、如果数据被读空,没有数据可读(n == 0),则 err 应该返回 EOF。
// 如果遇到读取错误,则 err 应该返回相应的错误信息。
type Reader interface {
    Read(p []byte) (n int, err error)
}

bufio.Reader 实现了该接口,其他的接口的实现请看上文,

package main

import (
	"bufio"
	"os"
	"fmt"
)

func main() {
	//func Copy(dst Writer, src Reader) (written int64, err error)
	path := "/Users/xinxingegeya/workspace-go/src/awesomeProject/io/hello.txt"
	fd, _ := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0666)

	rd := bufio.NewReaderSize(fd, 4096)

	fmt.Println(rd.Buffered()) //表示已经缓冲的数据的大小

	str, _ := rd.ReadString(byte('\n'))
	fmt.Println(str)
	fmt.Println(rd.Buffered()) //表示已经缓冲的数据的大小
}

 

io.WriterTo interface

// WriterTo 接口包装了基本的 WriteTo 方法,用于将自身的数据写入 w 中。
// 直到数据全部写入完毕或遇到错误为止,返回写入的字节数和遇到的错误。
type WriterTo interface {
    WriteTo(w Writer) (n int64, err error)
}

bufio.Reader 实现了该接口 ,使用示例如下,

package main

import (
	"bufio"
	"os"
	"fmt"
)

func main() {
	path := "/Users/xinxingegeya/workspace-go/src/awesomeProject/io/hello.txt"
	fd, _ := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0666)

	rd := bufio.NewReaderSize(fd, 4096)
	rd.ReadString(byte('\n'))
	fmt.Println(rd.Buffered()) //表示已经缓冲的数据的大小

	w := bufio.NewWriterSize(os.Stdout, 4096)
	rd.WriteTo(w) // WriteTo 方法实现了 io.WriterTo 接口
	fmt.Println(rd.Buffered())
	fmt.Println(w.Available())
	fmt.Println(w.Buffered())
	w.Flush()
}

 

io.ReaderFrom interface

// ReaderFrom 接口包装了基本的 ReadFrom 方法,用于从 r 中读取数据存入自身。
// 直到遇到 EOF 或读取出错为止,返回读取的字节数和遇到的错误。
type ReaderFrom interface {
    ReadFrom(r Reader) (n int64, err error)
}

bufio.Writer 实现了该接口,使用示例如下,

package main

import (
	"bufio"
	"os"
	"fmt"
)

func main() {
	path := "/Users/xinxingegeya/workspace-go/src/awesomeProject/io/hello.txt"
	fd, _ := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0666)

	rd := bufio.NewReaderSize(fd, 4096)
	rd.ReadString(byte('\n'))
	fmt.Println(rd.Buffered()) //表示已经缓冲的数据的大小

	w := bufio.NewWriterSize(os.Stdout, 4096)
	w.ReadFrom(rd)
	w.Flush()
}

 

io.Seeker interface

// Seeker 接口包装了基本的 Seek 方法,用于移动数据的读写指针。
// Seek 设置下一次读写操作的指针位置,每次的读写操作都是从指针位置开始的。
// whence 的含义:
// 如果 whence 为 0:表示从数据的开头开始移动指针。
// 如果 whence 为 1:表示从数据的当前指针位置开始移动指针。
// 如果 whence 为 2:表示从数据的尾部开始移动指针。
// offset 是指针移动的偏移量。
// 返回新指针位置和遇到的错误。
type Seeker interface {
    Seek(offset int64, whence int) (ret int64, err error)
}

strings.Reader 实现了 io.Seeker接口,

// A Reader implements the io.Reader, io.ReaderAt, io.Seeker, io.WriterTo,
// io.ByteScanner, and io.RuneScanner interfaces by reading
// from a string.
type Reader struct {
	s        string
	i        int64 // current reading index
	prevRune int   // index of previous rune; or < 0
}

// Seek implements the io.Seeker interface.
func (r *Reader) Seek(offset int64, whence int) (int64, error) {
	r.prevRune = -1
	var abs int64
	switch whence {
	case io.SeekStart:
		abs = offset
	case io.SeekCurrent:
		abs = r.i + offset
	case io.SeekEnd:
		abs = int64(len(r.s)) + offset
	default:
		return 0, errors.New("strings.Reader.Seek: invalid whence")
	}
	if abs < 0 {
		return 0, errors.New("strings.Reader.Seek: negative position")
	}
	r.i = abs
	return abs, nil
}

strings.Reader的使用示例,

package main

import (
	"strings"
	"io"
	"fmt"
)

func main() {
	rd := strings.NewReader("Hello World!")
	rd.Seek(6, io.SeekStart)
	data := make([]byte, 5)
	n, err := rd.Read(data)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(n)
	fmt.Println(string(data))
}

 

其他接口的概述

// 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。
type ReaderAt interface {
    ReadAt(p []byte, off int64) (n int, err error)
}

------------------------------

// WriterAt 接口包装了基本的 WriteAt 方法,用于将 p 中的数据写入自身。
// ReadAt 忽略之前的读写位置,从起始位置的 off 偏移处开始写入。
//
// 返回写入的字节数和遇到的错误。如果 p 没有被读完,则必须返回一个 err 值来说
// 明为什么没有读完。
//
// 如果 WriterAt 写入的对象是某个有偏移量的底层数据流时,则 ReadAt 方法既不能
// 影响底层的偏移量,也不应该被底层的偏移量影响。
//
// WriterAt 的调用者可以对同一数据流的不同区段并行执行 WriteAt 方法。
//
// WriterAt 的实现者不应该持有 p。
type WriterAt interface {
    WriteAt(p []byte, off int64) (n int, err error)
}

------------------------------

// ByteReader 接口包装了基本的 ReadByte 方法,用于从自身读出一个字节。
// 返回读出的字节和遇到的错误。
type ByteReader interface {
    ReadByte() (c byte, err error)
}

------------------------------

// ByteScanner 在 ByteReader 的基础上增加了一个 UnreadByte 方法,用于撤消最后
// 一次的 ReadByte 操作,以便下次的 ReadByte 操作可以读出与前一次一样的数据。
// UnreadByte 之前必须是 ReadByte 才能撤消成功,否则可能会返回一个错误信息(根
// 据不同的需求,UnreadByte 也可能返回 nil,允许随意调用 UnreadByte,但只有最
// 后一次的 ReadByte 可以被撤销,其它 UnreadByte 不执行任何操作)。
type ByteScanner interface {
    ByteReader
    UnreadByte() error
}

------------------------------

// ByteWriter 接口包装了基本的 WriteByte 方法,用于将一个字节写入自身
// 返回遇到的错误
type ByteWriter interface {
    WriteByte(c byte) error
}

------------------------------

// RuneReader 接口包装了基本的 ReadRune 方法,用于从自身读取一个 UTF-8 编码的
// 字符到 r 中。
// 返回读取的字符、字符的编码长度和遇到的错误。
type RuneReader interface {
    ReadRune() (r rune, size int, err error)
}

------------------------------

// RuneScanner 在 RuneReader 的基础上增加了一个 UnreadRune 方法,用于撤消最后
// 一次的 ReadRune 操作,以便下次的 ReadRune 操作可以读出与前一次一样的数据。
// UnreadRune 之前必须是 ReadRune 才能撤消成功,否则可能会返回一个错误信息(根
// 据不同的需求,UnreadRune 也可能返回 nil,允许随意调用 UnreadRune,但只有最
// 后一次的 ReadRune 可以被撤销,其它 UnreadRune 不执行任何操作)。
type RuneScanner interface {
    RuneReader
    UnreadRune() error
}

======END======


有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:xxggy

查看原文:Go io包

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4112 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传