Golang中输入和输出操作使用原语实现,这些原语将数据模拟称为可读或可写的字节流。为此,Golang的io
包提供了io.Reader
和io.Writer
接口,分别用于数据的输入和输出。

io
Golang的io
包提供了对I/O原语的基本接口,包装了原语的已有实现,使之成为共享的公共接口,抽象出泛用的函数并附加了相关的原语操作。这些接口和原语是对底层实现完全不同的低水平操作的包装,除非得到其它方面的通知,客户端不应该假设它们是并发执行安全的。
io.EOF
- EOF是
End-Of-File
的缩写,根据Go惯例大写字母缩写表示常量,不过io.EOF
被定义成了变量。 -
io.EOF
是io
包中的一个变量,表示文件结束的错误。
1package io23var EOF = errors.New("EOF")
-
io.EOF
是Go中最重要的错误变量,用于表示输入流的结尾,因为每个文件都有一个结尾,所以io.EOF
很多时候并不能算是一个错误,更重要是表示输入流结束了。
var EOF = errors.New("EOF")
io.Reader
-
io.Reader
表示一个读取器,将数据从某个资源读取到传输缓冲区。在缓冲区中,数据可以被流式传输和使用。

io.Reader
- 对于要用作读取器的类型,必须实现
io.Reader
接口的唯一方法Read(p []byte)
。只要实现了Read(p []byte)
就是一个读取器
type Reader interface{Read(p []byte) (n int, err error)
}
-
Read()
方法具有两个返回值,若资源内容全部读取完毕,应该返回io.EOF
错误。
返回值 | 类型 | 描述 |
---|---|---|
n | int | 读取到的字节数 |
err | error | 发生错误时的错误 |
利用io.Reader
可以实现流式数据传输,Reader
方法内部是被循环调用的,每次迭代,会从数据源读取一块数据放入缓冲区p
中,直到返回io.EOF
错误时停止。
例如:创建字符串读取器,流式地按字节读取。
str := "clear is better than clever"
//创建字符串读取器
reader := strings.NewReader(str)
//字节缓存区
buf := make([]byte, 4)
//循环读取
for{//读取到缓存区n,err := reader.Read(buf)if err!=nil{//读取结束if err!=io.EOF{fmt.Println("EOF:", n)break}fmt.Println(err)os.Exit(1)}result := string(buf[:n])fmt.Println(n, result)
}
4 clea
4 r is
4 bet
4 ter
4 than
4 cle
3 ver
EOF
例如:实现从流中过滤掉非字母字符
//过滤函数
func alpha(c byte) byte{if (c>='a'&&c<='z') || (c>='A'&&c<='Z'){return c}return 0
}
type AlphaReader struct{src string //资源i int//当前读取的位置
}
func NewAlphaReader(src string) *AlphaReader{return &AlphaReader{src:src}
}
func (r *AlphaReader) Read(p []byte) (int,error){//判断是否已经读取到结尾if r.i >= len(r.src){return 0, io.EOF}//剩余未读取长度x := len(r.src) - r.i//缓存区填满大小bound := 0//判断剩余长度是否超过缓存区大小if x >= len(p){//完全填满缓冲区bound = len(p)}else if x < len(p){//缓存区无法补满bound = x}//创建缓存区buf := make([]byte, bound)//迭代读取i := 0for bound > i {//每次读取一个字节char := r.src[r.i]//执行过滤函数if c := alpha(char); c!=0{buf[i] = c}i++r.i++}//将缓存内容赋值到copy(p, buf)return i,nil
}
测试
str := "Hello! It's 9am, where is the sun?"buf := make([]byte, 4)
reader := NewAlphaReader(str)
for {n,err := reader.Read(buf)if err==io.EOF{break}data := string(buf[:n])fmt.Print(data)
}
- 组合多个Reader可重用和屏蔽下层实现的复杂度
标准库已经实现了多个Reader,可使用一个Reader作为另一个Reader的实现是一种常见用法,可让一个Reader重用另一个Reader的逻辑。
例如:修改AlphaReader以接受io.Reader
作为其源
//过滤函数
func alpha(c byte) byte{if (c>='a'&&c<='z') || (c>='A'&&c<='Z'){return c}return 0
}
type AlphaReader struct{reader io.Reader//组合标准读取器
}
func NewAlphaReader(reader io.Reader) *AlphaReader{return &AlphaReader{reader:reader}
}
func (r *AlphaReader) Read(p []byte) (int,error){n,err := r.reader.Read(p)if err!=nil{return n,err}buf := make([]byte, n)for i:=0; i<n; i++{char := alpha(p[i])if char != 0{buf[i] = char}}copy(p, buf)return n,nil
}
测试
str := "Hello! It's 9am, where is the sun?"
reader := strings.NewReader(str)buf := make([]byte, 4)
r := NewAlphaReader(reader)
for {n,err := r.Read(buf)if err==io.EOF{break}data := string(buf[:n])fmt.Print(data)
}
例如:与os.File
结束过滤掉文件中的非字母字符
$ vim ./test.txt
Hello! It's 9am, where is the sun?
file,err := os.Open("./test.txt")
if err!=nil{fmt.Println(err)os.Exit(1)
}
defer file.Close()r := NewAlphaReader(file)
buf := make([]byte, 4)
for {n,err := r.Read(buf)if err==io.EOF{break}data := string(buf[:n])fmt.Print(data)
}
io.Writer
-
io.Writer
表示一个编写器,从缓存区读取数据,并将数据写入目标资源。

io.Writer
- 对于要作为编写器的类型必须实现
io.Writer
接口唯一的方法Write(p []byte)
,只要实现了Write(p []byte)
就是一个编写器。
type Writer interface{Write(p []byte) (n int, err error)
}
-
Write()
方法具有两个返回值,一个是写入到目标资源的字节数,一个是发生错误时的错误。
返回值 | 类型 | 描述 |
---|---|---|
n | int | 写入到目标资源的字节数 |
err | error | 发生错误时的错误 |
例如:使用bytes.Buffer
类型作为io.Writer
将数据写入内存缓存区
proverbs := []string{"many hands make light work","measure for measure","murder will out","never say die",
}var writer bytes.Buffer
for _,p := range proverbs{n,err := writer.Write([]byte(p))if err!=nil{fmt.Println(err)os.Exit(1)}if n != len(p){fmt.Println("failed to write data")os.Exit(1)}
}
result := writer.String()
fmt.Println(result)
例如:实现自定义的io.Writer
将其内容作为字节序列写入通道
//ChanWriter 通道写入器
type ChanWriter struct{ch chan byte//目标资源
}
//NewChanWriter 创建通道写入器
func NewChanWriter() *ChanWriter{return &ChanWriter{ch:make(chan byte, 1024)}
}
//Chan 获取目标资源
func (w *ChanWriter) Chan() <-chan byte{return w.ch
}
//Close 关闭目标资源
func (w *ChanWriter) Close(){close(w.ch)
}
//Write 写入目标资源
func (w *ChanWriter) Write(p []byte) (int, error){n := 0//遍历输入数据按字节写入目标资源for _,b := range p{w.ch<-bn++}return n,nil
}
测试
writer := NewChanWriter()go func(){defer writer.Close()writer.Write([]byte("money can talk"))
}()for char := range writer.Chan(){fmt.Printf("%c\n", char)
}
io.Closer
-
Closer
是包装基本Close()
方法的接口用于关闭数据流,第一次调用后Closer
的行为是未定义的。 - 文件、归档(压缩包)、数据库连接、Socket等均需手动关闭的资源都实现了
Closer
接口。 - 编程中经常会将Close方法的调用放在
defer
语句中
type Closer interface{Close() error //关闭数据流
}
例如:
file,err := os.Open("test.txt")
if err!=nil{}
defer file.Close()
io.ReadCloser
-
response.Body
的类型为io.ReadCloser
io.WriteCloser
io.ReadWriteCloser
相关文章
json to go
简介
在对接第三方或者某些功能重写的情况下,有json字符串,需要Go的结构体定义;当字段特别多的时候,费时费力。
此时可以使用json to go的这个在线工具,自动生成对应的Go结构体,方便而且不出错
jsontogo…
Dubbo-go v3.0 正式发布 ——打造国内一流开源 Go 服务框架
简介:Dubbo-go 是常新的,每年都在不断进化。介绍 Dubbo-go 3.0 工作之前,先回顾其过往 6 年的发展历程,以明晰未来的方向。 作者 | 李志信 来源 | 阿里技术公众号
作者介绍: 李志信(github laurencelizhix…
Java 不赚钱,要转 Go 吗?
发现一个 2021 年报告,显示 Go 是最想学习的编程语言 Go 到如今的火爆程度,不仅仅是因为有个好爹。上手快,一周就能入门上手,正常人写的 Go 都不会太差。有人说,它有一种迷人的气质,很难用语言表达…
java 自动转 golang_JAVA转Golang
GO语言因其并行机制出现在大众的视野中,Go编译到机器码非常的快速,它是快速的、静态类型编译语言,但是感觉上是动态类型,解释型语言,要说响应式编程应该reactive,webflux,RXJava,net…
idea String报错问题
一、下载问题 1.版本安装错误;
二、问题解决
1.彻底删除下载错误的idea
(1). 注册表清理
windows R 输入 regedit 打开注册表
点击一级菜单 HKEY_CURRENT_USER, 右键查找,输入idea,会找到jetbrains&…
Navicat 连接 mysql 报错
当我们用Navicat 连接数据库的时候,可能会遇到这个问题 : 这个问题主要是因为 5 和 8 的加密方式不一样,才导致报错! 解决方法 1、进入mysql的安装目录下的 bin 目录 2、mysql -h localhost -P3306 -uroot -p 3、执行 alter user…
安装tensorflow报错
安装tensorflow
使用管理员身份运行cmd 然后根据你的python版本安装对应的tensorflow (查看Python版本:python --version)
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow-gpu2.3.0如果上面的遇到403错误࿰…
yum makecache 报错
使用yum的时候总是提示The requested URL returned error: 404这个错误,经过一番折腾总算解决了。 首先说下yum源,使用的是163的源。http://mirrors.163.com/.help/CentOS7-Base-163.repo
[base]
nameCentOS-$releasever - Base - 163.com
baseurlhtt…
Navicat连接MySQL数据库报错1045
数据库创建后直接使用navicat工具进行连接报错1045 在保证你输入的账号密码都正确的情况下; 可能是因为你的正好没有权限 权限不够,因为MySQL默认数据库创建的账号权限仅限于localhost 需要进行授权
grant all privileges on *.* to username% identif…
安装weditor报错
weditor是app的定位工具,安装weditor报错如下:
跟着提示要更新一下pip c:\users\acer\appdata\local\programs\python\python38-32\python.exe -m pip install --upgrade pip
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple更新pip之后…
【web安全】——报错注入
作者名:Demo不是emo 主页面链接: 主页传送门 创作初心: 一切为了她 座右铭: 不要让时代的悲哀成为你的悲哀 专研方向: web安全,后渗透技术 每日emo: 内心的爱意逐渐被时间冲淡 今天给大家讲…
SpringBoot打包报错
一、对springBoot打包 package 报错的情况 处理方法:对文件目录下的pom.xml文件进行修改
① 修改编码 配置编码格式为 UTF-8
具体的操作就是在pom.xml文件中的<properties></properties>标签下增加一行配置编码格式的代码:
<propertie…
pip list 报错
使用到的具体命令: 1、python -m ensurepip 2、python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
项目场景:
pip list 报错
问题描述
报错如上图所示
原因分析: pip 更新时关闭窗口、断网、没有使用国…