在 io 包中最重要的是两个接口:Reader 和 Writer 接口。实现了这两个接口,就表明提供 I/O的功能

Reader接口

  1. // Reader is the interface that wraps the basic Read method.
  2. //
  3. // Read reads up to len(p) bytes into p. It returns the number of bytes
  4. // read (0 <= n <= len(p)) and any error encountered. Even if Read
  5. // returns n < len(p), it may use all of p as scratch space during the call.
  6. // If some data is available but not len(p) bytes, Read conventionally
  7. // returns what is available instead of waiting for more.
  8. //
  9. // When Read encounters an error or end-of-file condition after
  10. // successfully reading n > 0 bytes, it returns the number of
  11. // bytes read. It may return the (non-nil) error from the same call
  12. // or return the error (and n == 0) from a subsequent call.
  13. // An instance of this general case is that a Reader returning
  14. // a non-zero number of bytes at the end of the input stream may
  15. // return either err == EOF or err == nil. The next Read should
  16. // return 0, EOF.
  17. //
  18. // Callers should always process the n > 0 bytes returned before
  19. // considering the error err. Doing so correctly handles I/O errors
  20. // that happen after reading some bytes and also both of the
  21. // allowed EOF behaviors.
  22. //
  23. // Implementations of Read are discouraged from returning a
  24. // zero byte count with a nil error, except when len(p) == 0.
  25. // Callers should treat a return of 0 and nil as indicating that
  26. // nothing happened; in particular it does not indicate EOF.
  27. //
  28. // Implementations must not retain p.
  29. type Reader interface {
  30. Read(p []byte) (n int, err error)
  31. }

io.Reader接口的规则

  1. Read最多读取len(p)字节的数据,并保存到p。
  2. 返回读取的字节数以及任何发生的错误信息
  3. n要满足0 <= n <= len(p)
  4. n<len(p)时,表示读取的数据不足以填满p,这时方法会立即返回,而不是等待更多的数据
  5. 读取过程中遇到错误,会返回读取的字节数n以及相应的错误err
  6. 在底层输入流结束时,方法会返回n>0的字节,但是err可能时EOF,也可以是nil
  7. 在第6种(上面)情况下,再次调用read方法的时候,肯定会返回0,EOF
  8. 调用Read方法时,如果n>0时,优先处理处理读入的数据,然后再处理错误err,EOF也要这样处理
  9. Read方法不鼓励返回n=0并且err=nil的情况,
    ####Reader扩展接口
  1. type ReaderFrom interface {
  2. ReadFrom(r Reader) (n int64, err error)
  3. }

ReaderFrom 接口包装了基本的 ReadFrom 方法,用于从 r 中读取数据存入自身。 直到遇到 EOF 或读取出错为止,返回读取的字节数和遇到的错误。

  1. type ReaderAt interface {
  2. ReadAt(p []byte, off int64) (n int, err error)
  3. }
  1. type ByteReader interface {
  2. ReadByte() (c byte, err error)
  3. }

ByteReader 接口包装了基本的 ReadByte 方法,用于从自身读出一个字节。返回读出的字节和遇到的错误。

  1. type ByteScanner interface {
  2. ByteReader
  3. UnreadByte() error
  4. }

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

  1. type RuneReader interface {
  2. ReadRune() (r rune, size int, err error)
  3. }

RuneReader 接口包装了基本的 ReadRune 方法,用于从自身读取一个 UTF-8 编码的字符到 r 中。返回读取的字符、字符的编码长度和遇到的错误。

  1. type RuneScanner interface {
  2. RuneReader
  3. UnreadRune() error
  4. }

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

  1. package main
  2. import (
  3. "io"
  4. )
  5. type UpperString struct {
  6. s string
  7. i int64
  8. }
  9. func NewUpperString(s string)*UpperString{
  10. return &UpperString{s,0}
  11. }
  12. func (upperStr *UpperString)Len()int{
  13. if upperStr.i >= int64(len(upperStr.s)) {
  14. return 0
  15. }
  16. return int(int64(len(upperStr.s)) - upperStr.i)
  17. }
  18. func (upperStr *UpperString) Read(b []byte) (n int, err error) {
  19. for ; n<len(b)&&int(upperStr.i)<len(upperStr.s); upperStr.i++ {
  20. c := upperStr.s[upperStr.i]
  21. if c >= 'a' && c <= 'z' {
  22. c -= 'a' - 'A'
  23. }
  24. b[n] = c
  25. n++
  26. }
  27. if n == 0 {
  28. return n, io.EOF
  29. }
  30. return n, nil
  31. }
  32. func main() {
  33. str := NewUpperString("abcderghijkmnopq")
  34. b := make([]byte, str.Len())
  35. n, _ := str.Read(b)
  36. println(string(b[:n]))
  37. }

Writer 接口

  1. // Writer is the interface that wraps the basic Write method.
  2. //
  3. // Write writes len(p) bytes from p to the underlying data stream.
  4. // It returns the number of bytes written from p (0 <= n <= len(p))
  5. // and any error encountered that caused the write to stop early.
  6. // Write must return a non-nil error if it returns n < len(p).
  7. // Write must not modify the slice data, even temporarily.
  8. //
  9. // Implementations must not retain p.
  10. type Writer interface {
  11. Write(p []byte) (n int, err error)
  12. }
  1. write方法向底层数据流写入len(p)字节的数据,这些数据来自于切片p
  2. 返回被写入的字节数n,0 <= n <= len(p)
  3. 如果n<len(p), 则必须返回一些非nil的err
  4. 如果中途出现问题,也要返回非nil的err
  5. Write方法绝对不能修改切片p以及里面的数据

    Write扩展接口

  1. type WriterTo interface {
  2. WriteTo(w Writer) (n int64, err error)
  3. }

WriterTo 接口包装了基本的 WriteTo 方法,用于将自身的数据写入 w 中。直到数据全部写入完毕或遇到错误为止,返回写入的字节数和遇到的错误。

  1. type WriterTo interface {
  2. WriteTo(w Writer) (n int64, err error)
  3. }

WriterTo 接口包装了基本的 WriteTo 方法,用于将自身的数据写入 w 中。直到数据全部写入完毕或遇到错误为止,返回写入的字节数和遇到的错误。

  1. type ByteWriter interface {
  2. WriteByte(c byte) error
  3. }

ByteWriter 接口包装了基本的 WriteByte 方法,用于将一个字节写入自身

读取文件

  1. O_RDONLY //syscall.O_RDONLY(0x00000)只读
  2. O_WRONLY //syscall.O_WRONLY(0x00001)只写
  3. O_RDWR //syscall.O_RDWR(0x00002)可读可写
  4. O_CREATE //syscall.O_CREAT(0x00040)创建文件
  5. O_EXCL //syscall.O_EXCL(0x00080)配合O_CREATE使用,在创建文件时如果该文件已存在,则提示错误;open xxx: The file exists.
  6. O_TRUNC //syscall.O_TRUNC(0x00200)清零
  7. O_APPEND //syscall.O_APPEND(0x00400)续写
  8. O_SYNC //syscall.O_SYNC(0x01000)同步IO,表示对文件的更改会强制同步到硬盘,而不是写入缓存区后由系统写入硬盘

Closer

  1. type Closer interface {
  2. Close() error
  3. }
  4. Closer 接口包装了基本的 Close 方法,用于关闭数据读写。
  5. Close 一般用于关闭文件,关闭通道,关闭连接,关闭数据库等

Seeker

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

Seeker 接口包装了基本的 Seek 方法,用于移动数据的读写指针。Seek 设置下一次读写操作的指针位置,每次的读写操作都是从指针位置开始的。
whence 的含义是:
如果 whence 为 0:表示从数据的开头开始移动指针。
如果 whence 为 1:表示从数据的当前指针位置开始移动指针。
如果 whence 为 2:表示从数据的尾部开始移动指针。
offset 是指针移动的偏移量。
ret返回新指针位置

ioutil

  1. func readAll(r io.Reader, capacity int64) (b []byte, err error) {
  2. var buf bytes.Buffer
  3. // If the buffer overflows, we will get bytes.ErrTooLarge.
  4. // Return that as an error. Any other panic remains.
  5. defer func() {
  6. e := recover()
  7. if e == nil {
  8. return
  9. }
  10. if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
  11. err = panicErr
  12. } else {
  13. panic(e)
  14. }
  15. }()
  16. if int64(int(capacity)) == capacity {
  17. buf.Grow(int(capacity))
  18. }
  19. _, err = buf.ReadFrom(r)
  20. return buf.Bytes(), err
  21. }
  22. // ReadAll reads from r until an error or EOF and returns the data it read.
  23. // A successful call returns err == nil, not err == EOF. Because ReadAll is
  24. // defined to read from src until EOF, it does not treat an EOF from Read
  25. // as an error to be reported.
  26. func ReadAll(r io.Reader) ([]byte, error) {
  27. return readAll(r, bytes.MinRead)
  28. }
  29. // ReadFile reads the file named by filename and returns the contents.
  30. // A successful call returns err == nil, not err == EOF. Because ReadFile
  31. // reads the whole file, it does not treat an EOF from Read as an error
  32. // to be reported.
  33. func ReadFile(filename string) ([]byte, error) {
  34. f, err := os.Open(filename)
  35. if err != nil {
  36. return nil, err
  37. }
  38. defer f.Close()
  39. // It's a good but not certain bet that FileInfo will tell us exactly how much to
  40. // read, so let's try it but be prepared for the answer to be wrong.
  41. var n int64 = bytes.MinRead
  42. if fi, err := f.Stat(); err == nil {
  43. // As initial capacity for readAll, use Size + a little extra in case Size
  44. // is zero, and to avoid another allocation after Read has filled the
  45. // buffer. The readAll call will read into its allocated internal buffer
  46. // cheaply. If the size was wrong, we'll either waste some space off the end
  47. // or reallocate as needed, but in the overwhelmingly common case we'll get
  48. // it just right.
  49. if size := fi.Size() + bytes.MinRead; size > n {
  50. n = size
  51. }
  52. }
  53. return readAll(f, n)
  54. }
  55. // WriteFile writes data to a file named by filename.
  56. // If the file does not exist, WriteFile creates it with permissions perm;
  57. // otherwise WriteFile truncates it before writing.
  58. func WriteFile(filename string, data []byte, perm os.FileMode) error {
  59. f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
  60. if err != nil {
  61. return err
  62. }
  63. n, err := f.Write(data)
  64. if err == nil && n < len(data) {
  65. err = io.ErrShortWrite
  66. }
  67. if err1 := f.Close(); err == nil {
  68. err = err1
  69. }
  70. return err
  71. }
  72. // ReadDir reads the directory named by dirname and returns
  73. // a list of directory entries sorted by filename.
  74. func ReadDir(dirname string) ([]os.FileInfo, error) {
  75. f, err := os.Open(dirname)
  76. if err != nil {
  77. return nil, err
  78. }
  79. list, err := f.Readdir(-1)
  80. f.Close()
  81. if err != nil {
  82. return nil, err
  83. }
  84. sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
  85. return list, nil
  86. }
  87. type nopCloser struct {
  88. io.Reader
  89. }
  90. func (nopCloser) Close() error { return nil }
  91. // NopCloser returns a ReadCloser with a no-op Close method wrapping
  92. // the provided Reader r.
  93. func NopCloser(r io.Reader) io.ReadCloser {
  94. return nopCloser{r}
  95. }
  96. type devNull int
  97. // devNull implements ReaderFrom as an optimization so io.Copy to
  98. // ioutil.Discard can avoid doing unnecessary work.
  99. var _ io.ReaderFrom = devNull(0)
  100. func (devNull) Write(p []byte) (int, error) {
  101. return len(p), nil
  102. }
  103. func (devNull) WriteString(s string) (int, error) {
  104. return len(s), nil
  105. }
  106. var blackHolePool = sync.Pool{
  107. New: func() interface{} {
  108. b := make([]byte, 8192)
  109. return &b
  110. },
  111. }
  112. func (devNull) ReadFrom(r io.Reader) (n int64, err error) {
  113. bufp := blackHolePool.Get().(*[]byte)
  114. readSize := 0
  115. for {
  116. readSize, err = r.Read(*bufp)
  117. n += int64(readSize)
  118. if err != nil {
  119. blackHolePool.Put(bufp)
  120. if err == io.EOF {
  121. return n, nil
  122. }
  123. return
  124. }
  125. }
  126. }
  127. // Discard is an io.Writer on which all Write calls succeed
  128. // without doing anything.
  129. var Discard io.Writer = devNull(0)

copy

  1. src := strings.NewReader("1234567890")
  2. dst := new(strings.Builder)
  3. written, err := io.CopyN(dst, src, 8)
  4. if err != nil {
  5. fmt.Printf("error: %v\n", err)
  6. } else {
  7. fmt.Printf("Written(%d): %q\n", written, dst.String())
  8. }

接口组合

  1. type ReadWriter interface {
  2. Reader
  3. Writer
  4. }
  5. type ReadSeeker interface {
  6. Reader
  7. Seeker
  8. }
  9. type WriteSeeker interface {
  10. Writer
  11. Seeker
  12. }
  13. type ReadWriteSeeker interface {
  14. Reader
  15. Writer
  16. Seeker
  17. }
  18. type ReadCloser interface {
  19. Reader
  20. Closer
  21. }
  22. type WriteCloser interface {
  23. Writer
  24. Closer
  25. }
  26. type ReadWriteCloser interface {
  27. Reader
  28. Writer
  29. Closer
  30. }

附录

方法 说明
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