首页 > 其他分享 >golang 中通过strings/bytes/bufio 等包实现相关IO

golang 中通过strings/bytes/bufio 等包实现相关IO

时间:2023-04-25 17:55:16浏览次数:30  
标签:return io err bytes golang IO Reader buf

goIO 中,除了 io 、os 等包,我们还有 strings 、 bytes 、bufio 等实现 IO 读写,这其中有些实现了 io.Reader,有些同时实现了 io.Reader和io.Writer 接口。接下来我们一个个看相关的使用。

1.strings

strings包 中,只实现了 Reader,我们看看其代码:

// 实现的 reader 
type Reader struct {
	s        string
	i        int64 // current reading index
	prevRune int   // index of previous rune; or < 0
}

// 构造函数
// 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 { return &Reader{s, 0, -1} }

// Read implements the io.Reader interface.
func (r *Reader) Read(b []byte) (n int, err error) {
	if r.i >= int64(len(r.s)) {
		return 0, io.EOF
	}
	r.prevRune = -1
	n = copy(b, r.s[r.i:])
	r.i += int64(n)
	return
}

我们通过 NewReader(string) 构建一个 Reader 对象,随后就可以通过 Read() 读取 Reader 的内容,以下是其使用:

package main

import (
	"fmt"
	"strings"
)

func main()  {
	s := "Today is monday, what a great start!"
	sr := strings.NewReader(s)

	// read
	cap_sr := sr.Len()
	data := make([]byte, cap_sr)
	n, err := sr.Read(data)
	if err != nil {
		fmt.Printf("read to data failed, err: %s\n", err)
		return
	}
	fmt.Printf("read data size: %d, content: %s\n", n, data)
}

main函数结果

read data size: 36, content: Today is monday, what a great start!

2.bytes

bytes包 中,内部实现了 io.Reader,分别是 bytes.Readerbytes.Buffer,顾名思义,带 buffer 的就是有一定缓冲,但主要针对的还是 字节IO,下面看看其源码:

bytes.Reader

type Reader struct {
	s        []byte
	i        int64 // current reading index
	prevRune int   // index of previous rune; or < 0
}

// NewReader returns a new Reader reading from b.
func NewReader(b []byte) *Reader { return &Reader{b, 0, -1} }

// Read implements the io.Reader interface.
func (r *Reader) Read(b []byte) (n int, err error) {
	if r.i >= int64(len(r.s)) {
		return 0, io.EOF
	}
	r.prevRune = -1
	n = copy(b, r.s[r.i:])
	r.i += int64(n)
	return
}

其实我们也可以看到, bytesstringsReader 很像,只不过一个是字符串,一个是字节切片。

bytes.Buffer

// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	buf      []byte // contents are the bytes buf[off : len(buf)]
	off      int    // read at &buf[off], write at &buf[len(buf)]
	lastRead readOp // last read operation, so that Unread* can work correctly.
}

func NewBufferString(s string) *Buffer {
	return &Buffer{buf: []byte(s)}
}

func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} }


func (b *Buffer) Read(p []byte) (n int, err error) {
	b.lastRead = opInvalid
	if b.empty() {
		// Buffer is empty, reset to recover space.
		b.Reset()
		if len(p) == 0 {
			return 0, nil
		}
		return 0, io.EOF
	}
	n = copy(p, b.buf[b.off:])
	b.off += n
	if n > 0 {
		b.lastRead = opRead
	}
	return n, nil
}


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

可以看到,在这部分,提供了两个构造方法,通过字节切片或者字符串都可,对于 Read 方法其实和其他的都类似,主要是 Write 中,会将传入的数据追加给 Buffer.buf,所以再次读取内容时,可以看到是读到了新加的内容的。

使用示例

package main

import (
	"bytes"
	"fmt"
)

func main()  {
	// bytes.Reader
	fmt.Println("bytes.Reader test...")
	nr := bytes.NewReader([]byte("hello"))

	data := make([]byte, nr.Len())
	n, err := nr.Read(data)
	if err != nil {
		fmt.Printf("read to data failed, err: %s\n", err)
		return
	}
	fmt.Printf("read data1 size: %d, content: %s\n", n, data)

	// bytes.Buffer
	fmt.Println("bytes.Buffer test...")
	nbs := bytes.NewBufferString("world")
	nbs.Write([]byte(" lalalala..."))

	data2 := make([]byte, nbs.Len())
	n, err = nbs.Read(data2)
	if err != nil {
		fmt.Printf("read to data failed, err: %s\n", err)
		return
	}
	fmt.Printf("read data2 size: %d, content: %s\n", n, data2)
}

测试结果

bytes.Reader test...
read data1 size: 5, content: hello
bytes.Buffer test...
read data2 size: 17, content: world lalalala...

3.bufio

相较于前两者,在 bufio 中,实现的结构体就丰富很多,Reader、Writer、ReadWriter(结构体继承前2者) 都有实现,而且我们也可以通过 Writer 写入文件中,实现向文件中写入数据。

源码-Reader

// Reader implements buffering for an io.Reader object.
type Reader struct {
	buf          []byte
	rd           io.Reader // reader provided by the client
	r, w         int       // buf read and write positions
	err          error
	lastByte     int // last byte read for UnreadByte; -1 means invalid
	lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}

// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) *Reader {
	return NewReaderSize(rd, defaultBufSize)
}

// Read reads data into p.
// It returns the number of bytes read into p.
// The bytes are taken from at most one Read on the underlying Reader,
// hence n may be less than len(p).
// To read exactly len(p) bytes, use io.ReadFull(b, p).
// If the underlying Reader can return a non-zero count with io.EOF,
// then this Read method can do so as well; see the [io.Reader] docs.
func (b *Reader) Read(p []byte) (n int, err error) {
	n = len(p)
	if n == 0 {
		if b.Buffered() > 0 {
			return 0, nil
		}
		return 0, b.readErr()
	}
	if b.r == b.w {
		if b.err != nil {
			return 0, b.readErr()
		}
		if len(p) >= len(b.buf) {
			// Large read, empty buffer.
			// Read directly into p to avoid copy.
			n, b.err = b.rd.Read(p)
			if n < 0 {
				panic(errNegativeRead)
			}
			if n > 0 {
				b.lastByte = int(p[n-1])
				b.lastRuneSize = -1
			}
			return n, b.readErr()
		}
		// One read.
		// Do not use b.fill, which will loop.
		b.r = 0
		b.w = 0
		n, b.err = b.rd.Read(b.buf)
		if n < 0 {
			panic(errNegativeRead)
		}
		if n == 0 {
			return 0, b.readErr()
		}
		b.w += n
	}

	// copy as much as we can
	// Note: if the slice panics here, it is probably because
	// the underlying reader returned a bad count. See issue 49795.
	n = copy(p, b.buf[b.r:b.w])
	b.r += n
	b.lastByte = int(b.buf[b.r-1])
	b.lastRuneSize = -1
	return n, nil
}

// 按行读取内容
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
	line, err = b.ReadSlice('\n')
	if err == ErrBufferFull {
		// Handle the case where "\r\n" straddles the buffer.
		if len(line) > 0 && line[len(line)-1] == '\r' {
			// Put the '\r' back on buf and drop it from line.
			// Let the next call to ReadLine check for "\r\n".
			if b.r == 0 {
				// should be unreachable
				panic("bufio: tried to rewind past start of buffer")
			}
			b.r--
			line = line[:len(line)-1]
		}
		return line, true, nil
	}

	if len(line) == 0 {
		if err != nil {
			line = nil
		}
		return
	}
	err = nil

	if line[len(line)-1] == '\n' {
		drop := 1
		if len(line) > 1 && line[len(line)-2] == '\r' {
			drop = 2
		}
		line = line[:len(line)-drop]
	}
	return
}

源码-Writer

// Writer implements buffering for an io.Writer object.
// If an error occurs writing to a Writer, no more data will be
// accepted and all subsequent writes, and Flush, will return the error.
// After all data has been written, the client should call the
// Flush method to guarantee all data has been forwarded to
// the underlying io.Writer.
type Writer struct {
	err error
	buf []byte
	n   int
	wr  io.Writer
}

// NewWriter returns a new Writer whose buffer has the default size.
// If the argument io.Writer is already a Writer with large enough buffer size,
// it returns the underlying Writer.
func NewWriter(w io.Writer) *Writer {
	return NewWriterSize(w, defaultBufSize)
}

// Write writes the contents of p into the buffer.
// It returns the number of bytes written.
// If nn < len(p), it also returns an error explaining
// why the write is short.
func (b *Writer) Write(p []byte) (nn int, err error) {
	for len(p) > b.Available() && b.err == nil {
		var n int
		if b.Buffered() == 0 {
			// Large write, empty buffer.
			// Write directly from p to avoid copy.
			n, b.err = b.wr.Write(p)
		} else {
			n = copy(b.buf[b.n:], p)
			b.n += n
			b.Flush()
		}
		nn += n
		p = p[n:]
	}
	if b.err != nil {
		return nn, b.err
	}
	n := copy(b.buf[b.n:], p)
	b.n += n
	nn += n
	return nn, nil
}

// Flush writes any buffered data to the underlying io.Writer.
func (b *Writer) Flush() error {
	if b.err != nil {
		return b.err
	}
	if b.n == 0 {
		return nil
	}
	n, err := b.wr.Write(b.buf[0:b.n])
	if n < b.n && err == nil {
		err = io.ErrShortWrite
	}
	if err != nil {
		if n > 0 && n < b.n {
			copy(b.buf[0:b.n-n], b.buf[n:b.n])
		}
		b.n -= n
		b.err = err
		return err
	}
	b.n = 0
	return nil
}

这里需要注意的是,在 Writer 中调用完 Write 方法后,应该 Flush() 操作,才能将实际将数据转到 Writer.wr 中。

使用示例

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main()  {
	// read
	var filename string = "D:\\demo1\\src\\demo23\\go-io\\file\\file.txt"
	f, _ := os.Open(filename)
	defer f.Close()
	nr := bufio.NewReader(f)
	for {
		line, _, err := nr.ReadLine()
		fmt.Println(string(line))
		if err == io.EOF {
			break
		}
	}

	// write
	fw, _ := os.Create("bufio_w.txt")
	defer fw.Close()
	nw := bufio.NewWriter(fw)
	nw.WriteString("bufio write:\n")
	nw.WriteString("hello\n")
	nw.WriteString("world\n")
	nw.Flush()
}

测试结果

静夜思 - 唐·李白
床前看月光,疑是地上霜。
举头望山月,低头思故乡。

bufi_w.txt

bufio write:
hello
world

以上就是对 go 内置的各种 io 的总结,接下来将完善网络IO部分。

标签:return,io,err,bytes,golang,IO,Reader,buf
From: https://www.cnblogs.com/davis12/p/17353408.html

相关文章

  • apache storm ExclamationTopology例子
    1,packagestorm.starter.util;importbacktype.storm.Config;importbacktype.storm.LocalCluster;importbacktype.storm.generated.StormTopology;publicfinalclassStormRunner{privatestaticfinalintMILLIS_IN_SEC=1000;privateStormRunner(){......
  • iOS MachineLearning 系列(4)—— 静态图像分析之物体识别与分类
    iOSMachineLearning系列(4)——静态图像分析之物体识别与分类本系列的前几篇文件,详细了介绍了Vision框架中关于静态图片区域识别的内容。本篇文章,我们将着重介绍静态图片中物体的识别与分类。物体识别和分类也是MachineLearning领域重要的应用。通过大量的图片数据进行训练后,模......
  • mac使用Stable Diffussion进阶篇
    1、出图大法魔咒百科词典 (Bybilibili波西BrackRat)2、样例使用文字描述第一区►Prompt第一行(masterpiece:1.0),(bestquality:1.0),(ultrahighres:1.0),(8kresolution:1.0),(realistic:1.0),(ultradetailed1:0),(sharpfocus1:0),(RAWphoto:1.0)第二行1girl,......
  • java.lang.NoClassDefFoundError: org/apache/commons/io/output/UnsynchronizedByteA
    java.lang.NoClassDefFoundError:org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream  一、问题现象在导出Excel过程中,程序报错如下:Exceptioninthread"main"java.lang.NoClassDefFoundError:org/apache/commons/io/output/UnsynchronizedByteArra......
  • 如何建设一个用于编译 iOS App 的 macOS 云服务器集群?
    作者:京东零售叶萌现代软件开发一般会借助CI/CD来提升代码质量、加快发版速度、自动化重复的事情,iOSApp只能在mac机器上编译,CI/CD工具因此需要有一个macOS云服务器集群来执行iOSApp的编译。今天就来谈谈如何建设macOS云服务器集群购买macmini/MacStudio机......
  • golang 通过 os 包进行文件读写
    go中os包主要与操作系统打交道,实际底层还是通过相关的系统调用实现文件的读写操作,今天我们就来聊聊通过os包实现文件的读写操作。我们在使用os包进行相关操作时,主要流程:读操作open->read->close写操作open->read->write->close总体来说,读写操作都......
  • DTIOT物联网中台
    将可视化和IOT(物联网)智能感知技术与一站式运维管理理念相结合,以可视化平台为核心,将企业空间管理、设备设施和管线全生命周期管理、环境监测管理、安防/消防、等通过“可视化、集成化、智能化”的技术手段建立一站式智慧运维平台,实现以“安全、高效、节能、便利”为目标。平台架......
  • Cloud Studio 一个好用的在线编程工具
    CloudStudio是一个可咸可甜、可分工可协作,无论高端玩家、新手入门、编程学习皆适用的在线编程工具。使用时无需额外安装,打开浏览器,便能随时随地在线编程,妈妈再也不用担心我电脑没电、网线被拔啦!除了包含代码高亮、自动补全、Git集成、终端等IDE的基础功能外,CloudStudio还......
  • vite + vue3 + vue-router4 + ts + element plus + pinia + axios构建项目
    最后是完整的vite.config.ts、main.ts配置1、先用vite创建一个项目npmcreatevite@latest2、安装elementplusyarnaddelement-plus@element-plus/icons-vuevite.config.ts配置组件按需导入,图标自动导入npminstall-Dunplugin-vue-componentsunplugin-auto-impor......
  • Consider defining a bean of type 'org.springframework.security.authentication.Au
    Considerdefiningabeanoftype'org.springframework.security.authentication.AuthenticationManager'inyourconfiguration.[2023-04-2514:44:36.426][main][ERROR]o.s.b.diagnostics.LoggingFailureAnalysisReporter-***************************......