在 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 {
ByteReader
UnreadByte() 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 {
RuneReader
UnreadRune() error
}
RuneScanner 在 RuneReader 的基础上增加了一个 UnreadRune 方法,用于撤消最后一次的 ReadRune 操作,以便下次的 ReadRune 操作可以读出与前一次一样的数据。
UnreadRune 之前必须是 ReadRune 才能撤消成功,否则可能会返回一个错误信息(根据不同的需求,UnreadRune 也可能返回 nil,允许随意调用 UnreadRune,但只有最后一次的 ReadRune 可以被撤销,其它 UnreadRune 不执行任何操作)。
自定义实现reader接口
package main
import (
"io"
)
type UpperString struct {
s string
i 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] = c
n++
}
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.MinRead
if 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 := 0
for {
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 {
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
}
附录
方法 | 说明 |
---|---|
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 |