golang 中io包用法(一)


原文链接: golang 中io包用法(一)

golang 中io包用法(一)

本文转自Golove博客:http://www.cnblogs.com/golove/p/3276678.html   ,并在此基础上进行修改.

io 包为I/O原语提供了基础的接口.它主要包装了这些原语的已有实现,如 os 包中的那些,抽象成函数性的共享公共接口,加上一些其它相关的原语。

由于这些接口和原语以不同的实现包装了低级操作,因此除非另行通知,否则客户不应假定它们对于并行执行是安全的。

在io包中最重要的是两个接口:Reader和Writer接口,首先来介绍这两个接口.

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

Reader 接口包装了基本的 Read 方法。

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

当 Read 在成功读取 n > 0 个字节后遇到一个错误或 EOF 情况,它就会返回读取的字节数。它会从相同的调用中返回(非nil的)错误或从随后的调用中返回错误(和 n == 0)。这种一般情况的一个例子就是 Reader 在输入流结束时会返回一个非零的字节数,可能的返回不是 err == EOF 就是 err == nil。无论如何,下一个 Read 都应当返回 0, EOF。

调用者应当总在考虑到错误 err 前处理 n > 0 的字节。这样做可以在读取一些字节,以及允许的 EOF 行为后正确地处理I/O错误。

Read 的实现会阻止返回零字节的计数和一个 nil 错误,调用者应将这种情况视作空操作。

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

Writer 接口包装了基本的 Write 方法。

Write 将 len(p) 个字节从 p 中写入到基本数据流中。它返回从 p 中被写入的字节数n(0 <= n <= len(p))以及任何遇到的引起写入提前停止的错误。若 Write 返回的n < len(p),它就必须返回一个非nil的错误。Write 不能修改此切片的数据,即便它是临时的。

Io包中的函数(方法):
func ReadFull(r Reader, buf []byte) (n int, err error)
  这个函数可以把对象 r 中的数据读出来,然后存入一个缓冲区 buf 中,以便其它代码可以处理 buf 中的数据。
  这里有个问题,ReadFull 函数究竟可以读取哪些对象的数据?可以读文件中的数据吗?可以读网络中的数据吗?可以读数据库中的数据吗?可以读磁盘中的扇区吗?可以读内存中的数据吗?
  答案是 ReadFull 可以读取任何对象的数据,但是有个前提,就是这个对象必须符合 Reader 的标准。
  Reader 的标准是什么呢?下面是 Reader 的定义:

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

  从上面的定义可以看出,Reader 的标准很简单,只要某个对象实现了 Read 方法,这个对象就符合了 Reader 的标准,就可以被 ReadFull 读取。
  太简单了,只需要实现 Read 方法,不需要做其它任何事情。下面我们就来定义一个自己的类型,然后实现 Read 方法:

// 定义一个 Ustr 类型(以 string 为基类型)
type Ustr string

// 实现 Ustr 类型的 Read 方法
func (s Ustr) Read(p []byte) (n int, err error) {
    i, ls, lp := 0, len(s), len(p)
    for ; i < ls && i < lp; i++ {
        // 将小写字母转换为大写字母,然后写入 p 中
        if s[i] >= 'a' && s[i] <= 'z' {
            p[i] = s[i] + 'A' - 'a'
        } else {
            p[i] = s[i]
        }
    }
    // 根据读取的字节数设置返回值
    switch i {
    case lp:
        return i, nil
    case ls:
        return i, io.EOF
    default:
        return i, errors.New("Read Fail")
    }
}

  接下来,我们就可以用 ReadFull 方法读取 Ustr 对象的数据了:

func main() {
    us := Ustr("Hello World!")     // 创建 Ustr 对象 us
    buf := make([]byte, 32)        // 创建缓冲区 buf
    n, err := io.ReadFull(us, buf) // 将 us 中的数据读取到 buf 中
    fmt.Printf("%s\n", buf)        // 显示 buf 中的内容
    // HELLO WORLD!
    fmt.Println(n, err) // 显示返回值
    // 12 unexpected EOF
}

  我们很快就实现了 Reader 的要求,这个 Reader 就是一个接口,接口就是一个标准,一个要求,一个规定,这个规定就是“要实现接口中的方法”。只要某个对象符合 Reader 接口的要求,那么这个对象就可以当作 Reader 接口来使用,就可以传递给 ReadFull 方法。
  所以,只要文件对象实现了 Read 方法,那么 ReadFull 就可以读取文件中的数据,只要网络对象实现了 Read 方法,ReadFull 就可以读取网络中的数据,只要数据库实现了 Read 方法,ReadFull 就可以读取数据库中的数据,只要磁盘对象实现了 Read 方法,ReadFull 就可以读磁盘中的数据,只要内存对象实现了 Read 方法,ReadFull 就可以读取内存中的数据,只要任何一个对象实现了 Read 方法,ReadFull 就可以读取该对象的数据。

  在 io 包中,定义了许多基本的接口类型,Go 语言的标准库中大量使用了这些接口(就像 ReadFull 一样使用它们),下面我们就来看一看都有哪些接口:

// Reader 接口封装了基本的 Read 方法
// Read 方法用于将对象的数据流读入到 p 中
// 直到“p 被装满”或者“数据被读完”或者“数据读取出错”为止
// 返回读取的字节数 n (0 <= n <= len(p)) 和读取过程中遇到的任何错误
// 如果“p 被装满”,则 err 应该返回 nil
// 如果“数据被读完”,则 err 应该返回 EOF
// 如果“数据读取出错”,则 err 应该返回相应的错误信息
// Reader 用来输出数据

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

// Writer 接口封装了基本的 Write 方法
// Write 方法用于将 p 中的数据写入到对象的数据流中
// 返回写入的字节数 n (0 <= n <= len(p)) 和写入时遇到的任何错误
// 如果 p 中的数据全部被写入,则 err 应该返回 nil
// 如果 p 中的数据无法被全部写入,则 err 应该返回相应的错误信息
// Writer 用来存入数据

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

// Closer 接口封装了基本的 Close 方法
// Close 一般用于关闭文件,关闭连接,关闭数据库等
// Close 用来关闭数据

type Closer interface {
    Close() error
}

// Seeker 接口封装了基本的 Seek 方法
// Seek 设置下一次读写操作的指针位置,每次的读写操作都是从指针位置开始的
// whence 的含义:
// 如果 whence 为 0:表示从数据的开头开始移动指针
// 如果 whence 为 1:表示从数据的当前指针位置开始移动指针
// 如果 whence 为 2:表示从数据的尾部开始移动指针
// offset 是指针移动的偏移量
// 返回移动后的指针位置和移动过程中遇到的任何错误
// Seeker 用来移动数据的读写指针

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

// 下面是这些接口的组合接口

type ReadWriter interface {
    Reader
    Writer
}

type ReadSeeker interface {
    Reader
    Seeker
}

type WriteSeeker interface {
    Writer
    Seeker
}

type ReadWriteSeeker interface {
    Reader
    Writer
    Seeker
}

type ReadCloser interface {
    Reader
    Closer
}

type WriteCloser interface {
    Writer
    Closer
}

type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

// ReaderFrom 接口封装了基本的 ReadFrom 方法
// ReadFrom 从 r 中读取数据到对象的数据流中
// 直到 r 返回 EOF 或 r 出现读取错误为止
// 返回值 n 是读取的字节数
// 返回值 err 就是 r 的返回值 err
// ReadFrom 用来读出 r 中的数据

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

// WriterTo 接口封装了基本的 WriteTo 方法
// WriterTo 将对象的数据流写入到 w 中
// 直到对象的数据流全部写入完毕或遇到写入错误为止
// 返回值 n 是写入的字节数
// 返回值 err 就是 w 的返回值 err
// WriteTo 用来将数据写入 w 中

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

// ReaderAt 接口封装了基本的 ReadAt 方法
// ReadAt 从对象数据流的 off 处读出数据到 p 中
// 忽略数据的读写指针,从数据的起始位置偏移 off 处开始读取
// 如果对象的数据流只有部分可用,不足以填满 p
// 则 ReadAt 将等待所有数据可用之后,继续向 p 中写入
// 直到将 p 填满后再返回
// 在这点上 ReadAt 要比 Read 更严格
// 返回读取的字节数 n 和读取时遇到的错误
// 如果 n < len(p),则需要返回一个 err 值来说明
// 为什么没有将 p 填满(比如 EOF)
// 如果 n = len(p),而且对象的数据没有全部读完,则
// err 将返回 nil
// 如果 n = len(p),而且对象的数据刚好全部读完,则
// err 将返回 EOF 或者 nil(不确定)

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

// WriterAt 接口封装了基本的 WriteAt 方法
// WriteAt 将 p 中的数据写入到对象数据流的 off 处
// 忽略数据的读写指针,从数据的起始位置偏移 off 处开始写入
// 返回写入的字节数和写入时遇到的错误
// 如果 n < len(p),则必须返回一个 err 值来说明
// 为什么没有将 p 完全写入

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

// ByteReader 接口封装了基本的 ReadByte 方法
// ReadByte 从对象的数据流中读取一个字节到 c 中
// 如果对象的数据流中没有可读数据,则返回一个错误信息

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

// ByteScanner 在 ByteReader 的基础上增加了一个 UnreadByte 方法
// UnreadByte 用于撤消最后一次的 ReadByte 操作
// 即将对象的读写指针移到上次 ReadByte 之前的位置
// 如果上一次的操作不是 ReadByte,则 UnreadByte 返回一个错误信息

type ByteScanner interface {
ByteReader
UnreadByte() error
}

// ByteWriter 接口封装了基本的 WriteByte 方法
// WriteByte 将一个字节 c 写入到对象的数据流中
// 返回写入过程中遇到的任何错误

type ByteWriter interface {
    WriteByte(c byte) error
}

// RuneReader 接口封装了基本的 ReadRune 方法
// ReadRune 从对象的数据流中读取一个字符到 r 中
// 如果对象的数据流中没有可读数据,则返回一个错误信息

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

// RuneScanner 在 RuneReader 的基础上增加了一个 UnreadRune 方法
// UnreadRune 用于撤消最后一次的 ReadRune 操作
// 即将对象的读写指针移到上次 ReadRune 之前的位置
// 如果上一次的操作不是 ReadRune,则 UnreadRune 返回一个错误信息

type RuneScanner interface {
    RuneReader
    UnreadRune() error
}

// bytes.NewBuffer 实现了很多基本的接口,可以通过 bytes 包学习接口的实现

func main() {
    bb := bytes.NewBuffer([]byte("Hello World!"))
    b := make([]byte, 32)

    bb.Read(b)
    fmt.Printf("%s\n", b) // Hello World!

    bb.WriteString("New Data!\n")
    bb.WriteTo(os.Stdout) // New Data!

    bb.WriteString("Third Data!")
    bb.ReadByte()
    fmt.Println(bb.String()) // hird Data!
    bb.UnreadByte()
    fmt.Println(bb.String()) // Third Data!
}

// 下面是 io 包中的函数

// WriteString 将字符串 s 写入到 w 中
// 返回写入的字节数和写入过程中遇到的任何错误
// 如果 w 实现了 WriteString 方法
// 则调用 w 的 WriteString 方法将 s 写入 w 中
// 否则,将 s 转换为 []byte
// 然后调用 w.Write 方法将数据写入 w 中
func WriteString(w Writer, s string) (n int, err error)

func main() {
    // os.Stdout 实现了 Writer 接口
    io.WriteString(os.Stdout, "Hello World!")
    // Hello World!
}

// ReadAtLeast 从 r 中读取数据到 buf 中,要求至少读取 min 个字节
// 返回读取的字节数 n 和读取过程中遇到的任何错误
// 如果 n < min,则 err 返回 ErrUnexpectedEOF
// 如果 r 中无可读数据,则 err 返回 EOF
// 如果 min 大于 len(buf),则 err 返回 ErrShortBuffer
// 只有当 n >= min 时 err 才返回 nil
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)

func main() {
    r := strings.NewReader("Hello World!")
    b := make([]byte, 32)
    n, err := io.ReadAtLeast(r, b, 20)
    fmt.Printf("%s\n%d, %v", b, n, err)
    // Hello World!
    // 12, unexpected EOF
}

// ReadFull 的功能和 ReadAtLeast 一样,只不过 min = len(buf),其中要求最少读取的字节数目是len(buf),当r中数据少于len(buf)时便会报错
func ReadFull(r Reader, buf []byte) (n int, err error)

func main() {
    r := strings.NewReader("Hello World!")
    b := make([]byte, 32)
    n, err := io.ReadFull(r, b)
    fmt.Printf("%s\n%d, %v", b, n, err)
    // Hello World!
    // 12, unexpected EOF
}

// CopyN 从 src 中复制 n 个字节的数据到 dst 中
// 它返回复制的字节数 written 和复制过程中遇到的任何错误
// 只有当 written = n 时,err 才返回 nil
// 如果 dst 实现了 ReadFrom 方法,则调用 ReadFrom 来执行复制操作
func CopyN(dst Writer, src Reader, n int64) (written int64, err error)

func main() {
    r := strings.NewReader("Hello World!")
    n, err := io.CopyN(os.Stdout, r, 20)
    fmt.Printf("\n%d, %v", n, err)
    // Hello World!
    // 12, EOF
}

// Copy 从 src 中复制数据到 dst 中,直到所有数据复制完毕
// 返回复制过程中遇到的任何错误
// 如果数据复制完毕,则 err 返回 nil,而不是 EOF
// 如果 dst 实现了 ReadeFrom 方法,则调用 dst.ReadeFrom(src) 复制数据
// 如果 src 实现了 WriteTo 方法,则调用 src.WriteTo(dst) 复制数据
func Copy(dst Writer, src Reader) (written int64, err error)

func main() {
    r := strings.NewReader("Hello World!")
    n, err := io.Copy(os.Stdout, r)
    fmt.Printf("\n%d, %v", n, err)
    // Hello World!
    // 12, <nil>
}

// LimitReader 覆盖了 r 的 Read 方法
// 使 r 只能读取 n 个字节的数据,读取完毕后返回 EOF
func LimitReader(r Reader, n int64) Reader
// LimitedReader 结构用来实现 LimitReader 的功能
type LimitedReader struct

func main() {
    r := strings.NewReader("Hello World!")
    lr := io.LimitReader(r, 5)
    n, err := io.CopyN(os.Stdout, lr, 20)
    fmt.Printf("\n%d, %v", n, err)
    // Hello
    // 5, EOF
}

// NewSectionReader 封装 r,并返回 SectionReader 类型的对象
// 使 r 只能从 off 的位置读取 n 个字节的数据,读取完毕后返回 EOF
func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader
// SectionReader 结构用来实现 NewSectionReader 的功能
// SectionReader 实现了 Read、Seek、ReadAt、Size 方法
type SectionReader struct
// Size 返回 s 中被限制读取的字节数
func (s *SectionReader) Size()

func main() {
    r := strings.NewReader("Hello World!")
    sr := io.NewSectionReader(r, 0, 5)
    n, err := io.CopyN(os.Stdout, sr, 20)
    fmt.Printf("\n%d, %v", n, err)
    fmt.Printf("\n%d", sr.Size())
    // World
    // 5, EOF
    // 5
}

// TeeReader 覆盖了 r 的 Read 方法
// 使 r 在读取数据的同时,自动向 w 中写入数据
// 所有写入时遇到的错误都被作为 err 返回值
func TeeReader(r Reader, w Writer) Reader

func main() {
    r := strings.NewReader("Hello World!")
    tr := io.TeeReader(r, os.Stdout)
    b := make([]byte, 32)
    tr.Read(b)
    // World World!
}
`