在 io 包中最重要的是两个接口:Reader 和 Writer 接口。实现了这两个接口,就表明提供 I/O的功能
Reader接口
// Reader is the interface that wraps the basic Read method.//// Read reads up to len(p) bytes into p. It returns the number of bytes// read (0 <= n <= len(p)) and any error encountered. Even if Read// returns n < len(p), it may use all of p as scratch space during the call.// If some data is available but not len(p) bytes, Read conventionally// returns what is available instead of waiting for more.//// When Read encounters an error or end-of-file condition after// successfully reading n > 0 bytes, it returns the number of// bytes read. It may return the (non-nil) error from the same call// or return the error (and n == 0) from a subsequent call.// An instance of this general case is that a Reader returning// a non-zero number of bytes at the end of the input stream may// return either err == EOF or err == nil. The next Read should// return 0, EOF.//// Callers should always process the n > 0 bytes returned before// considering the error err. Doing so correctly handles I/O errors// that happen after reading some bytes and also both of the// allowed EOF behaviors.//// Implementations of Read are discouraged from returning a// zero byte count with a nil error, except when len(p) == 0.// Callers should treat a return of 0 and nil as indicating that// nothing happened; in particular it does not indicate EOF.//// Implementations must not retain p.type Reader interface {Read(p []byte) (n int, err error)}
io.Reader接口的规则
- Read最多读取len(p)字节的数据,并保存到p。
- 返回读取的字节数以及任何发生的错误信息
- n要满足0 <= n <= len(p)
- n<len(p)时,表示读取的数据不足以填满p,这时方法会立即返回,而不是等待更多的数据
- 读取过程中遇到错误,会返回读取的字节数n以及相应的错误err
- 在底层输入流结束时,方法会返回n>0的字节,但是err可能时EOF,也可以是nil
- 在第6种(上面)情况下,再次调用read方法的时候,肯定会返回0,EOF
- 调用Read方法时,如果n>0时,优先处理处理读入的数据,然后再处理错误err,EOF也要这样处理
- Read方法不鼓励返回n=0并且err=nil的情况,
####Reader扩展接口
type ReaderFrom interface {ReadFrom(r Reader) (n int64, err error)}
ReaderFrom 接口包装了基本的 ReadFrom 方法,用于从 r 中读取数据存入自身。 直到遇到 EOF 或读取出错为止,返回读取的字节数和遇到的错误。
type ReaderAt interface {ReadAt(p []byte, off int64) (n int, err error)}
type ByteReader interface {ReadByte() (c byte, err error)}
ByteReader 接口包装了基本的 ReadByte 方法,用于从自身读出一个字节。返回读出的字节和遇到的错误。
type ByteScanner interface {ByteReaderUnreadByte() error}
ByteScanner 在 ByteReader 的基础上增加了一个 UnreadByte 方法,用于撤消最后一次的 ReadByte 操作,以便下次的 ReadByte 操作可以读出与前一次一样的数据。
UnreadByte 之前必须是 ReadByte 才能撤消成功,否则可能会返回一个错误信息(根据不同的需求,UnreadByte 也可能返回 nil,允许随意调用 UnreadByte,但只有最后一次的 ReadByte 可以被撤销,其它 UnreadByte 不执行任何操作)
type RuneReader interface {ReadRune() (r rune, size int, err error)}
RuneReader 接口包装了基本的 ReadRune 方法,用于从自身读取一个 UTF-8 编码的字符到 r 中。返回读取的字符、字符的编码长度和遇到的错误。
type RuneScanner interface {RuneReaderUnreadRune() error}
RuneScanner 在 RuneReader 的基础上增加了一个 UnreadRune 方法,用于撤消最后一次的 ReadRune 操作,以便下次的 ReadRune 操作可以读出与前一次一样的数据。
UnreadRune 之前必须是 ReadRune 才能撤消成功,否则可能会返回一个错误信息(根据不同的需求,UnreadRune 也可能返回 nil,允许随意调用 UnreadRune,但只有最后一次的 ReadRune 可以被撤销,其它 UnreadRune 不执行任何操作)。
自定义实现reader接口
package mainimport ("io")type UpperString struct {s stringi int64}func NewUpperString(s string)*UpperString{return &UpperString{s,0}}func (upperStr *UpperString)Len()int{if upperStr.i >= int64(len(upperStr.s)) {return 0}return int(int64(len(upperStr.s)) - upperStr.i)}func (upperStr *UpperString) Read(b []byte) (n int, err error) {for ; n<len(b)&&int(upperStr.i)<len(upperStr.s); upperStr.i++ {c := upperStr.s[upperStr.i]if c >= 'a' && c <= 'z' {c -= 'a' - 'A'}b[n] = cn++}if n == 0 {return n, io.EOF}return n, nil}func main() {str := NewUpperString("abcderghijkmnopq")b := make([]byte, str.Len())n, _ := str.Read(b)println(string(b[:n]))}
Writer 接口
// Writer is the interface that wraps the basic Write method.//// Write writes len(p) bytes from p to the underlying data stream.// It returns the number of bytes written from p (0 <= n <= len(p))// and any error encountered that caused the write to stop early.// Write must return a non-nil error if it returns n < len(p).// Write must not modify the slice data, even temporarily.//// Implementations must not retain p.type Writer interface {Write(p []byte) (n int, err error)}
- write方法向底层数据流写入len(p)字节的数据,这些数据来自于切片p
- 返回被写入的字节数n,0 <= n <= len(p)
- 如果n<len(p), 则必须返回一些非nil的err
- 如果中途出现问题,也要返回非nil的err
- Write方法绝对不能修改切片p以及里面的数据
Write扩展接口
type WriterTo interface {WriteTo(w Writer) (n int64, err error)}
WriterTo 接口包装了基本的 WriteTo 方法,用于将自身的数据写入 w 中。直到数据全部写入完毕或遇到错误为止,返回写入的字节数和遇到的错误。
type WriterTo interface {WriteTo(w Writer) (n int64, err error)}
WriterTo 接口包装了基本的 WriteTo 方法,用于将自身的数据写入 w 中。直到数据全部写入完毕或遇到错误为止,返回写入的字节数和遇到的错误。
type ByteWriter interface {WriteByte(c byte) error}
ByteWriter 接口包装了基本的 WriteByte 方法,用于将一个字节写入自身
读取文件
O_RDONLY //syscall.O_RDONLY(0x00000)只读O_WRONLY //syscall.O_WRONLY(0x00001)只写O_RDWR //syscall.O_RDWR(0x00002)可读可写O_CREATE //syscall.O_CREAT(0x00040)创建文件O_EXCL //syscall.O_EXCL(0x00080)配合O_CREATE使用,在创建文件时如果该文件已存在,则提示错误;open xxx: The file exists.O_TRUNC //syscall.O_TRUNC(0x00200)清零O_APPEND //syscall.O_APPEND(0x00400)续写O_SYNC //syscall.O_SYNC(0x01000)同步IO,表示对文件的更改会强制同步到硬盘,而不是写入缓存区后由系统写入硬盘
Closer
type Closer interface {Close() error}Closer 接口包装了基本的 Close 方法,用于关闭数据读写。Close 一般用于关闭文件,关闭通道,关闭连接,关闭数据库等
Seeker
type Seeker interface {Seek(offset int64, whence int) (ret int64, err error)}
Seeker 接口包装了基本的 Seek 方法,用于移动数据的读写指针。Seek 设置下一次读写操作的指针位置,每次的读写操作都是从指针位置开始的。
whence 的含义是:
如果 whence 为 0:表示从数据的开头开始移动指针。
如果 whence 为 1:表示从数据的当前指针位置开始移动指针。
如果 whence 为 2:表示从数据的尾部开始移动指针。
offset 是指针移动的偏移量。
ret返回新指针位置
ioutil
func readAll(r io.Reader, capacity int64) (b []byte, err error) {var buf bytes.Buffer// If the buffer overflows, we will get bytes.ErrTooLarge.// Return that as an error. Any other panic remains.defer func() {e := recover()if e == nil {return}if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {err = panicErr} else {panic(e)}}()if int64(int(capacity)) == capacity {buf.Grow(int(capacity))}_, err = buf.ReadFrom(r)return buf.Bytes(), err}// ReadAll reads from r until an error or EOF and returns the data it read.// A successful call returns err == nil, not err == EOF. Because ReadAll is// defined to read from src until EOF, it does not treat an EOF from Read// as an error to be reported.func ReadAll(r io.Reader) ([]byte, error) {return readAll(r, bytes.MinRead)}// ReadFile reads the file named by filename and returns the contents.// A successful call returns err == nil, not err == EOF. Because ReadFile// reads the whole file, it does not treat an EOF from Read as an error// to be reported.func ReadFile(filename string) ([]byte, error) {f, err := os.Open(filename)if err != nil {return nil, err}defer f.Close()// It's a good but not certain bet that FileInfo will tell us exactly how much to// read, so let's try it but be prepared for the answer to be wrong.var n int64 = bytes.MinReadif fi, err := f.Stat(); err == nil {// As initial capacity for readAll, use Size + a little extra in case Size// is zero, and to avoid another allocation after Read has filled the// buffer. The readAll call will read into its allocated internal buffer// cheaply. If the size was wrong, we'll either waste some space off the end// or reallocate as needed, but in the overwhelmingly common case we'll get// it just right.if size := fi.Size() + bytes.MinRead; size > n {n = size}}return readAll(f, n)}// WriteFile writes data to a file named by filename.// If the file does not exist, WriteFile creates it with permissions perm;// otherwise WriteFile truncates it before writing.func WriteFile(filename string, data []byte, perm os.FileMode) error {f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)if err != nil {return err}n, err := f.Write(data)if err == nil && n < len(data) {err = io.ErrShortWrite}if err1 := f.Close(); err == nil {err = err1}return err}// ReadDir reads the directory named by dirname and returns// a list of directory entries sorted by filename.func ReadDir(dirname string) ([]os.FileInfo, error) {f, err := os.Open(dirname)if err != nil {return nil, err}list, err := f.Readdir(-1)f.Close()if err != nil {return nil, err}sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })return list, nil}type nopCloser struct {io.Reader}func (nopCloser) Close() error { return nil }// NopCloser returns a ReadCloser with a no-op Close method wrapping// the provided Reader r.func NopCloser(r io.Reader) io.ReadCloser {return nopCloser{r}}type devNull int// devNull implements ReaderFrom as an optimization so io.Copy to// ioutil.Discard can avoid doing unnecessary work.var _ io.ReaderFrom = devNull(0)func (devNull) Write(p []byte) (int, error) {return len(p), nil}func (devNull) WriteString(s string) (int, error) {return len(s), nil}var blackHolePool = sync.Pool{New: func() interface{} {b := make([]byte, 8192)return &b},}func (devNull) ReadFrom(r io.Reader) (n int64, err error) {bufp := blackHolePool.Get().(*[]byte)readSize := 0for {readSize, err = r.Read(*bufp)n += int64(readSize)if err != nil {blackHolePool.Put(bufp)if err == io.EOF {return n, nil}return}}}// Discard is an io.Writer on which all Write calls succeed// without doing anything.var Discard io.Writer = devNull(0)
copy
src := strings.NewReader("1234567890")dst := new(strings.Builder)written, err := io.CopyN(dst, src, 8)if err != nil {fmt.Printf("error: %v\n", err)} else {fmt.Printf("Written(%d): %q\n", written, dst.String())}
接口组合
type ReadWriter interface {ReaderWriter}type ReadSeeker interface {ReaderSeeker}type WriteSeeker interface {WriterSeeker}type ReadWriteSeeker interface {ReaderWriterSeeker}type ReadCloser interface {ReaderCloser}type WriteCloser interface {WriterCloser}type ReadWriteCloser interface {ReaderWriterCloser}
附录
| 方法 | 说明 |
|---|---|
| func OpenFile(name string, flag int, perm FileMode) (*File, error) | 通用文件打开函数,其中多个标志位可采取||方式携带,perm为文件权限 |
| func Open(name string) (*File, error) | 以只读方式打开文件O_RDONLY |
| func Create(name string) (*File, error) | 创建文件,且打开文件方式为可读、可写、trunc |
| func (f *File) Seek(offset int64, whence int) (ret int64, err error) | 返回offset,其中whence可指定offset是相对于哪里(0表示文件开头,1表示当前offset,2表示文件结尾),需要注意的是,对于以APPEND模式打开的文件,官方文档表示行为不确定。The behavior of Seek on a file opened with O_APPEND is not specified. |
| func (f *File) Read(b []byte) (n int, err error) | |
| func (f *File) ReadAt(b []byte, off int64) (n int, err error) | |
| func (f *File) Write(b []byte) (n int, err error) | |
| func (f *File) WriteAt(b []byte, off int64) (n int, err error) | |
| func Rename(oldname string, newname string) (err error) | //文件改名,若newname已存在则覆盖原有文件,即删除newname再将oldname改名;注意文件名可携带相对或绝对路径且更改前后文件夹路径应一致,否则会被拒绝操作;注意Windows下路径的“\”需要转义 |
| func Chdir(dir string) error | 更改当前工作文件夹 |
| func Mkdir(name string, perm FileMode) error | 创建文件夹 |
| func Chmod(name string, mode FileMode) error | |
| func (f *File) Chmod(mode FileMode) error |
