golang 中通过 strings/bytes/bufio 等包实现相关IO
在 go
的 IO
中,除了 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.Reader
和 bytes.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 }
其实我们也可以看到, bytes
和 strings
的 Reader
很像,只不过一个是字符串,一个是字节切片。
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
部分。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
2021-04-25 3w字详解java集合
2021-04-25 ELK实时日志分析平台环境部署--完整记录(转)
2021-04-25 ELK简介(转)