Golang中输入和输出操作使用原语实现,这些原语将数据模拟称为可读或可写的字节流。为此,Golang的io
包提供了io.Reader
和io.Writer
接口,分别用于数据的输入和输出。
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
接口的唯一方法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 := 0
for 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
接口唯一的方法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<-b
n++
}
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
有疑问加站长微信联系(非本文作者)