IO Streaming

Streaming IO in Go,引用此文,略有修改

io.Reader和io.Writer

io.Reader接口定义了从传输缓存读取数据

type Reader interface {
Read(p []byte) (n int, err error)
}

Read方法接受一个[]byte作为读取数据后的接收者,返回读取的字节数n和错误err,当读尽缓存内容时,err为io.EOF。

Read方法的读取机制

  1. 尽可能读取len(p)字节到p
  2. 调用Read()后,n可能比len(p)小
  3. 如果出现错误,read()仍可能返回缓冲区p中的n个字节。例如,读取突然关闭的TCP套接字。根据您的使用,您可以选择将字节保持在p中或重试。
  4. 当Read()读尽数据后,可能返回非零的n和err=io.EOF。甚至出现返回非零n和err=nil的情况,但是接下来的Read()方法调用一定返回n=0, err=io.EOF
  5. 当n=0,err=nil时,并不意味着读取数据完毕,接下来的Read()可能返回更多的数据。

综上,实现Read()方法还是比较棘手的,幸好标准库有好多实现了io.Reader的接口。

strings.NewReader
// NewReader returns a new Reader reading from s.
// It is similar to bytes.NewBufferString
// but more efficient and read-only.
func NewReader(s string) *Reader func main() {
reader := strings.NewReader("Clear is better than clever")
p := make([]byte, 4) for {
n, err := reader.Read(p)
if err != nil{
if err == io.EOF {
fmt.Println(string(p[:n])) //should handle any remainding bytes.
break
}
fmt.Println(err)
os.Exit(1)
}
fmt.Println(string(p[:n]))
}
}
自定义io.Reader

实现Read()方法,去除非字母

type alphaReader struct {
src string
cur int
} func newAlphaReader(src string) *alphaReader {
return &alphaReader{src: src}
} func alpha(r byte) byte {
if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
return r
}
return 0
} func (a *alphaReader) Read(p []byte) (int, error) {
if a.cur >= len(a.src) {
return 0, io.EOF
} x := len(a.src) - a.cur
n, bound := 0, 0
if x >= len(p) {
bound = len(p)
} else if x <= len(p) {
bound = x
} buf := make([]byte, bound)
for n < bound {
if char := alpha(a.src[a.cur]); char != 0 {
buf[n] = char
}
n++
a.cur++
}
copy(p, buf) // 使用copy方法,保证切片每次读取指定大小的内容
return n, nil
} func main() {
reader := newAlphaReader("Hello! It's 9am, where is the sun?")
p := make([]byte, 4)
for {
n, err := reader.Read(p)
if err == io.EOF {
break
}
fmt.Print(string(p[:n]))
}
fmt.Println()
}

借助标准库的io.Reader实现来简化代码

type alphaReader struct {
reader io.Reader
} func newAlphaReader(reader io.Reader) *alphaReader {
return &alphaReader{reader: reader}
} func alpha(r byte) byte {
if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
return r
}
return 0
} func (a *alphaReader) Read(p []byte) (int, error) {
n, err := a.reader.Read(p)
if err != nil {
return n, err
}
buf := make([]byte, n)
for i := 0; i < n; i++ {
if char := alpha(p[i]); char != 0 {
buf[i] = char
}
} copy(p, buf)
return n, nil
} func main() {
// use an io.Reader as source for alphaReader
reader := newAlphaReader(strings.NewReader("Hello! It's 9am, where is the sun?"))
p := make([]byte, 4)
for {
n, err := reader.Read(p)
if err == io.EOF {
break
}
fmt.Print(string(p[:n]))
}
fmt.Println()
}

与读取文件的io.Reader结合

func main() {
// use an os.File as source for alphaReader
file, err := os.Open("./alpha_reader3.go")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close() reader := newAlphaReader(file)
p := make([]byte, 4)
for {
n, err := reader.Read(p)
if err == io.EOF {
break
}
fmt.Print(string(p[:n]))
}
fmt.Println()
}

io.Writer

type Writer interface {
Write(p []byte) (n int, err error)
}

Write方法接受一个[]byte作为输入

func main() {
proverbs := []string{
"Channels orchestrate mutexes serialize",
"Cgo is not Go",
"Errors are values",
"Don't panic",
}
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)
}
} fmt.Println(writer.String())
}
实现自定义io.Writer
type chanWriter struct {
ch chan byte
} func newChanWriter() *chanWriter {
return &chanWriter{make(chan byte, 1024)}
} func (w *chanWriter) Chan() <-chan byte {
return w.ch
} func (w *chanWriter) Write(p []byte) (int, error) {
n := 0
for _, b := range p {
w.ch <- b
n++
}
return n, nil
} func (w *chanWriter) Close() error {
close(w.ch)
return nil
} func main() {
writer := newChanWriter()
go func() {
defer writer.Close()
writer.Write([]byte("Stream "))
writer.Write([]byte("me!"))
}()
for c := range writer.Chan() {
fmt.Printf("%c", c)
}
fmt.Println()
}

chanWriter也实现了io.Closer, 调用writer.Close()关闭channel,避免chanWriter.Write

方法一直等待channel。

os.File

亦实现了io.Writer和io.Reader接口,因此向文件写文件可以调用writer.Write([]byte), os.Create返回*os.File

func main() {
proverbs := []string{
"Channels orchestrate mutexes serialize\n",
"Cgo is not Go\n",
"Errors are values\n",
"Don't panic\n",
}
// func Create(name string) (*File, error)
file, err := os.Create("./proverbs.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close() for _, p := range proverbs {
n, err := file.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)
}
}
fmt.Println("file write done")
}

os.Open返回的*os.File是只读的

func main() {
// 如果需要读写文件,使用os.OpenFile
// file, err := os.OpenFile("./proverbs.txt", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
file, err := os.Open("./proverbs.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close() p := make([]byte, 4)
for {
n, err := file.Read(p)
if err == io.EOF {
break
}
fmt.Print(string(p[:n]))
}
}

os.Stdout, os.Stdin, os.Stderr 其实也是*os.File

func main() {
proverbs := []string{
"Channels orchestrate mutexes serialize\n",
"Cgo is not Go\n",
"Errors are values\n",
"Don't panic\n",
} for _, p := range proverbs {
n, err := os.Stdout.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)
}
}
}

io.Copy 方便的将流数据拷贝到目标reader

func main() {
proverbs := new(bytes.Buffer)
proverbs.WriteString("Channels orchestrate mutexes serialize\n")
proverbs.WriteString("Cgo is not Go\n")
proverbs.WriteString("Errors are values\n")
proverbs.WriteString("Don't panic\n") file, err := os.Create("./proverbs.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close() // copy from reader data into writer file
if _, err := io.Copy(file, proverbs); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("file created")
}
// 或者
func main() {
file, err := os.Open("./proverbs.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close() if _, err := io.Copy(os.Stdout, file); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

io.WriteString()

可以方便地将字符串写入到writer

func main() {
file, err := os.Create("./magic_msg.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close()
if _, err := io.WriteString(file, "Go is fun!"); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

Pipe writers and readers

输入io.PipeWriter和io.PipeReader模型IO操作,如在内存管道中。数据被写入管道的写入端,并使用单独的GO例程在管道的读取器端读取数据。

func main() {
proverbs := new(bytes.Buffer)
proverbs.WriteString("Channels orchestrate mutexes serialize\n")
proverbs.WriteString("Cgo is not Go\n")
proverbs.WriteString("Errors are values\n")
proverbs.WriteString("Don't panic\n") piper, pipew := io.Pipe() // write in writer end of pipe
go func() {
defer pipew.Close()
io.Copy(pipew, proverbs)
}() // read from reader end of pipe.
io.Copy(os.Stdout, piper)
piper.Close()
}

Buffered IO

GO通过包bufio支持缓冲IO,这使得使用文本内容更加容易。例如,下面的程序读取以值‘\n’分隔的逐行文件的内容。

func main() {
file, err := os.Open("./planets.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close()
reader := bufio.NewReader(file) for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
} else {
fmt.Println(err)
os.Exit(1)
}
}
fmt.Print(line)
} }

Util package

包ioutil是io的一个子包,它为IO提供了一些方便的功能。例如,以下代码使用函数ReadFile将文件的内容加载到[]byte中

package main

import (
"io/ioutil"
...
) func main() {
bytes, err := ioutil.ReadFile("./planets.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("%s", bytes)
}

还有其他IO Stream 操作

  • file IO
  • buffered IO
  • network IO
  • formatted IO

golang IO streaming的更多相关文章

  1. 简析 Golang IO 包

    简析 Golang IO 包 io 包提供了 I/O 原语(primitives)的基本接口.io 包中定义了四个最基本接口 Reader.Writer.Closer.Seeker 用于表示二进制流的 ...

  2. golang io中io.go解读

    目录 1. 整体大纲 2. 接口 读 写 关闭 寻址 3. 函数 读 写 复制 4. 结构体 SectionReader LimitedReader teeReader 5. 备注 根据golang ...

  3. Golang IO包的妙用

    Golang 标准库对 IO 的抽象非常精巧,各个组件可以随意组合,可以作为接口设计的典范.这篇文章结合一个实际的例子来和大家分享一下. 背景 以一个RPC的协议包来说,每个包有如下结构 type P ...

  4. golang io需要牢记的几个点

    对于Reader比较麻烦需要记住以下: When Read encounters an error or end-of-file condition after successfully readin ...

  5. golang io.ReadFull

    buf := make([]byte, 10, 10) file, _ := os.Open("./data.txt") n, err := io.ReadFull(file, b ...

  6. 【Linux开发】IO streaming DMA buffer importing

    http://linuxtv.org/downloads/v4l-dvb-apis/dmabuf.html I/O流 (DMA缓存引用) 这是一个实验性接口,将来可能发生改变 DMABUF框架提供了在 ...

  7. golang io操作之写篇

    /** * @author livalon * @data 2018/9/4 15:11 */ package main import ( "os" "fmt" ...

  8. 实现golang io.Writer支持按照天为单位分割日志

    golang中的日志不支持按照天分割,很多开源的日志包都是只支持按照文件大小分割日志,不太利于生产环境中的使用.因此我实现了timewriter,支持: 实现按照天为单位分割日志,可以完美支持gola ...

  9. golang IO 流抽象与应用

    https://blog.csdn.net/pmlpml/article/details/82930191

随机推荐

  1. Django学习笔记(进阶篇)

    Django学习笔记(进阶篇):http://www.cnblogs.com/wupeiqi/articles/5246483.html

  2. javascript和c#aes加密方法互解

    关键信息如下. javascript function Encrypt() { var key = CryptoJS.enc.Utf8.parse('8080808080808080'); var i ...

  3. Spring-bean的自动装配

    bean的自动装配:可以让一个bean对象自动的引用其他bean byType:按照类型进行装配.  缺点:如果在IOC容器中存在多个类型相同的bean的时候,会出现异常. <bean id=& ...

  4. Eclipse neon 4.6 安装tomcat

    问题: Eclipse neon 4.6并没有内置Tomcat,所以当我产生想要导入.war,并部署到服务器时,会看到创建服务处是下面的情况: 也就是说,没有tomcat服务可以选择:为此我需配置To ...

  5. P3806 【模板】点分治1

    一道淀粉质的模版题,开始是暴力 #include <bits/stdc++.h> #define up(i,l,r) for(register int i = (l); i <= ( ...

  6. Executors创建的4种线程池的使用

    Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...

  7. web测试和app测试的区别

    功能上: 功能上没有什么区别,都是用同样的方法来写用例(等效.边界值...) 架构上: web是B/S架构(浏览器和服务器)代码更新后数据会同步,可以保证所有客户一致 app是C/S架构(客户端和服务 ...

  8. python之路(十七)-javascript

    JavaScript JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的处理. ...

  9. struts2 升级至2.3.32时访问页面报错 File "/struts-tags" not found

    Apache struts是美国阿帕奇(Apache)软件基金会负责维护的一个开源项目,是一套用于创建企业级Java Web 应用的开源MVC框架,主要提供两个版本框架产品: struts 1和str ...

  10. Vue route部分简单高级用法

    一改变页面title的值   在开发时常常需要在切换到不同页面时改变浏览器的title值,那么我们就可以在定义路由的时候通过配置 meta 属性 来改变title值. import Vue from ...