千家信息网

Go语言中的Writer和Reader怎么使用

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,今天小编给大家分享一下Go语言中的Writer和Reader怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收
千家信息网最后更新 2026年11月04日Go语言中的Writer和Reader怎么使用

今天小编给大家分享一下Go语言中的Writer和Reader怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

输入和输出

Go Writer 和 Reader接口的设计遵循了Unix的输入和输出,一个程序的输出可以是另外一个程序的输入。他们的功能单一并且纯粹,这样就可以非常容易的编写程序代码,又可以通过组合的概念,让我们的程序做更多的事情。

var (    Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")    Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")    Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)

这三种标准的输入和输出都是一个*File,而*File恰恰就是同时实现了io.Writer和io.Reader这两个接口的类型,所以它们同时具备输入和输出的功能,既可以从里面读取数据,又可以往里面写入数据。

Go标准库的io包也是基于Unix这种输入和输出的理念,大部分的接口都是扩展了io.Writer和io.Reader,大部分的类型也都选择地实现了io.Writer和io.Reader这两个接口,然后把数据的输入和输出,抽象为流的读写。所以只要实现了这两个接口,都可以使用流的读写功能。

io.Writer和io.Reader两个接口的高度抽象,让我们不用再面向具体的业务,我们只关注,是读还是写。只要我们定义的方法函数可以接收这两个接口作为参数,那么我们就可以进行流的读写,而不用关心如何读、写到哪里去,这也是面向接口编程的好处。

Reader和Writer接口

这两个高度抽象的接口,只有一个方法,也体现了Go接口设计的简洁性,只做一件事。

// 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)
}

这是Wirter接口的定义,它只有一个Write方法。它接受一个byte的切片,返回两个值,n表示写入的字节数、err表示写入时发生的错误。

从其文档注释来看,这个方法是有规范要求的,我们要想实现一个io.Writer接口,就要遵循这些规则。

  • write方法向底层数据流写入len(p)字节的数据,这些数据来自于切片p;

  • 返回被写入的字节数n,0 <= n <= len(p);

  • 如果n

  • 如果中途出现问题,也要返回非nil的err;

  • Write方法绝对不能修改切片p以及里面的数据。

这些实现io.Writer接口的规则,所有实现了该接口的类型都要遵守,不然可能会导致莫名其妙的问题。

// 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方法。这个方法接受一个byte的切片,并返回两个值,一个是读入的字节数,一个是err错误。

从其注释文档看,io.Reader接口的规则更多。

  • Read最多读取len(p)字节的数据,并保存到p;

  • 返回读取的字节数以及任何发生的错误信息;

  • n要满足0 <= n <= len(p);

  • n

  • 读取过程中遇到错误,会返回读取的字节数n以及相应的错误err;

  • 在底层输入流结束时,方法会返回n>0的字节,但是err可能时EOF,也可以是nil;

  • 在第6种(上面)情况下,再次调用read方法的时候,肯定会返回0,EOF;

  • 调用Read方法时,如果n>0时,优先处理处理读入的数据,然后再处理错误err,EOF也要这样处理;

  • Read方法不鼓励返回n=0并且err=nil的情况。

规则稍微比Write接口有点多,不过也都比较好理解。注意第 8 条,即使我们在读取的时候遇到错误,但是也应该处理已经读到的数据。因为这些已经读到的数据是正确的,如果不进行处理丢失的话,读到的数据就不完整了。

示例

对这两个接口了解后,我们就可以尝试使用他们了,现在来看个例子。

func main() {    //定义零值Buffer类型变量b    var b bytes.Buffer    //使用Write方法为写入字符串    b.Write([]byte("你好"))        //这个是把一个字符串拼接到Buffer里    fmt.Fprint(&b,",","http://www.flysnow.org")        //把Buffer里的内容打印到终端控制台    b.WriteTo(os.Stdout)
}

这个例子是拼接字符串到Buffer里,然后再输出到控制台。它非常简单,但是利用了流的读写,bytes.Buffer是一个可变字节的类型,可以让我们很容易的对字节进行操作,比如读写、追加等。bytes.Buffer实现了io.Writer和io.Reader接口,所以我们可以很容易地进行读写操作,而不用关注具体实现。

b.Write([]byte("你好"))实现了写入一个字符串。我们把这个字符串转为一个字节切片,然后调用Write方法写入,这个就是bytes.Buffer为了实现io.Writer接口而实现的一个方法,可以帮我们写入数据流。

func (b *Buffer) Write(p []byte) (n int, err error) {    b.lastRead = opInvalid    m := b.grow(len(p))    return copy(b.buf[m:], p), nil
}

以上就是bytes.Buffer实现io.Writer接口的方法。最终我们看到,写入的切片会被拷贝到b.buf里,这里b.buf[m:]拷贝其实就是追加的意思,不会覆盖已经存在的数据。

从实现看,我们发现其实只有b *Buffer指针实现了io.Writer接口,所以我们示例代码中调用fmt.Fprint函数的时候,传递的是一个地址&b。

func Fprint(w io.Writer, a ...interface{}) (n int, err error) {    p := newPrinter()    p.doPrint(a)    n, err = w.Write(p.buf)    p.free()    return
}

这是函数fmt.Fprint的实现,它的功能就是实现把数据a写入到一个io.Writer接口,具体如何写入,它是不关心的。因为这都是io.Writer会做的,它只关心可以写入即可。w.Write(p.buf)调用Wirte方法写入。

最后的b.WriteTo(os.Stdout)是把最终的数据输出到标准的os.Stdout里,以便我们查看输出,它接收一个io.Writer接口类型的参数。开篇我们讲过os.Stdout也实现了这个io.Writer接口,所以就可以作为参数传入。

这里我们会发现,很多方法的接收参数都是io.Writer接口,当然还有io.Reader接口,这就是面向接口的编程。我们不用关注具体实现,只关注这个接口可以做什么事情。如果我们换成输出到文件里,那也很容易,只需把os.File类型作为参数即可。任何实现了该接口的类型,都可以作为参数。

除了b.WriteTo方法外,我们还可以使用io.Reader接口的Read方法实现数据的读取。

var p [100]byte
n,err:=b.Read(p[:])
fmt.Println(n,err,string(p[:n]))

这是最原始的方法,使用Read方法,n为读取的字节数,然后我们输出打印出来。

因为byte.Buffer指针实现了io.Reader接口,所以我们还可以使用如下方式读取数据信息。

data,err:=ioutil.ReadAll(&b)
fmt.Println(string(data),err)

ioutil.ReadAll接收了一个io.Reader接口的参数,表明可以从任何实现了io.Reader接口的类型里读取全部的数据。

func readAll(r io.Reader, capacity int64) (b []byte, err error) {    buf := bytes.NewBuffer(make([]byte, 0, capacity))    // 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) } }() _, err = buf.ReadFrom(r) return buf.Bytes(), err
}

以上就是"Go语言中的Writer和Reader怎么使用"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

接口 方法 数据 字节 输出 两个 类型 输入 参数 就是 错误 字符 字符串 处理 不用 功能 只有 更多 知识 程序 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技海报 哈尔滨大商圈互联网科技有限公司 广州大一互联网络科技有限公司 深圳互联网科技公司 以房互联网科技 小马过河互联网科技 电子科技大学互联网 四川狮子座互联网科技 广州慧亚互联网科技怎么样 互联网科技教育视频 华服互联网科技官网 互联网金融科技展 特约风豹互联网科技为什么扣费 工业互联网展湾科技股份有限公司 美国互联网科技实力 厦门江牛科技互联网 互联网科技接地气 中国新注册互联网科技 互联网科技职业技能培训学校 承德科技产业互联网平台 广州互联网络科技有限公司 丰台科技园 互联网 居家互联网科技有限公司 北京华百购互联网科技有限公司 互联网企业和科技型企业 速维有品互联网信息科技(苏州)有限公司 想开互联网科技公司怎么样 互联网方面的科技前沿 张家口科技产业互联网平台 好易买互联网科技
0