zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", b) // Hello World!
    
    	bb.WriteString("New Data!
    ")
    	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
    %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
    %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("
    %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("
    %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("
    %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("
    %d, %v", n, err)
    	fmt.Printf("
    %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!
    }

  • 相关阅读:
    PHP数组处理总结
    设计模式之-工厂模式理解
    我的世界观
    编程入门
    2019 新的一年
    placeholder 不支持ie8
    2018年8月20日
    HttpClientUtil
    通用mapper
    small_demo
  • 原文地址:https://www.cnblogs.com/msnsj/p/4242598.html
Copyright © 2011-2022 走看看