赞
踩
输入输出(I/O)是一个程序最基础的一部分。而Golang中涉及io的包非常多,对于小白很容易混淆,现在就来花一点时间来把梳理清楚。
本文将主要涉及以下的几个包
io
io.ioutil
bufio
bytes
strings
net
os
首先需要从高层次上来区分开这几个包。在官网Standard library · std · pkg.go.dev中,这几个包的区别和侧重点如下
标准库 | 描述 | 侧重点 |
---|---|---|
io | 提供了I/O原语的基本接口 | 定义基本的接口 |
io.ioutil | 实现了一些I/O实用函数 | 为提供方便,封装了一些实用的I/O函数 |
bufio | 实现了缓冲的I/O | 实现了带缓冲的I/O (例如一些大文件) |
bytes | 实现了操作字节切片的功能 | 主要针对字节切片的,里面实现了bytes.Reader 和 bytes.Buffer |
strings | 实现了简单的函数来操作UTF-8编码的字符串 | 主要针对字符串的,里面实现了string.Reader |
net | 为网络I/O提供了一个可移植的接口,包括TCP/IP、UDP、域名解析和Unix域套接字 | 网络句柄,其中的net.conn 实现了Reader ,Writer 等 |
os | 提供了一个独立于平台的操作系统功能接口 | 文件句柄,其中的os.File os.Stdio os.Stdout os.Stderr 实现了Reader ,Writer 等 |
io
io库是GO语言中的核心,其中定义了IO操作中的相关接口。
io.Reader
与io.Writer
在io中最基础的就是读和写, 这两个的接口如下:
type Reader interface {
Read(p []byte) (n int, err error) //接收一个字节切片p,将读取的数据放入到p中,返回读取了的字节个数
}
type Writer interface {
Write(p []byte) (n int, err error) //接收一个字节切片p,将p中的数据写入到文件或文件中,返回写入了的字节个数
}
这俩个接口定义了最基本的读与写。
io.Seeker
与io.Closer
在io中,定义了 io.Seeker
与io.Closer
来设置光标和关闭。
type Seeker interface {
Seek(offset int64, whence int) (int64, error) // 设置相对于whence偏移offset的读或者写的偏移量,返回相对于文件开始的新偏移量和一个错误
}
type Closer interface {
Close() error //关闭数据流
}
io.ReaderAt
和io.WriterAt
io.ReaderAt
和io.WriterAt
两个接口在io.Reader
与io.Writer
的基础上增加了指定偏移量的操作
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error) //从基本输入源的偏移量 off 处开始,将 len(p) 个字节读取到 p 中,它返回读取的字节数 n(0 <= n <= len(p))以及任何遇到的错误。
}
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error) //从 p 中将 len(p) 个字节写入到偏移量 off 处的基本数据流中。它返回从 p 中被写入的字节数 n(0 <= n <= len(p))以及任何遇到的引起写入提前停止的错误
}
io.ReaderFrom
和 io.WiterTo
io.ReaderFrom
和io.WriterTo
两个接口相比于io.Reader
与io.Writer
,指定了各自的输入和输出的对象
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error) //从 r 中读取数据,直到 EOF 或发生错误。其返回值 n 为读取的字节数
}
type WriterTo interface {
WriteTo(w Writer) (n int64, err error) //将数据写入 w 中,直到没有数据可写或发生错误。其返回值 n 为写入的字节数
}
io库中也针对byte、Rune,string类型的数据实现了各自的Reader和Writer
type ByteReader interface {
ReadByte() (c byte, err error) //读一个字节
}
type ByteWriter interface {
WriteByte(c byte) error //写一个字节
}
type ByteScanner interface {
ByteReader // 内嵌了 ByteReader 接口
UnreadByte() error // 将上一次 ReadByte 的字节还原
}
type RuneReader interface {
ReadRune() (r rune, size int, err error) //读一个字符r 返回尺寸size 和错误
}
type RuneScanner interface {
RuneReader
UnreadRune() error
}
注意:没有RuneWriter接口,据说是开发者认为几乎不需要RuneWriter接口
type StringWriter interface {
WriteString(s string) (n int, err error) //字符串s的内容写到w
}
上面的都是一些单一的接口,而有些时候同时需要莫两个或者三个的接口的所有功能,所以在io库中提供了这些“小接口”组合而成的“大接口”。
以上的接口可以归纳为下图
在不同的标准库场景中,分别实现了io
库中的接口:
bufio
中实现了带缓冲的io
os
中实现了file,stdin,stdout,stderr的io
net
中实现了网络io
strings
中实现了字符串io
bytes
中实现了字节io
compress,crypto …
除了接口,io库中还有相对应的一些变量、方法和接口实现(后续讲)。
io库中的变量主要是一些错误。
EOF
没有更多输入可以读取ErrClosePipe
读取或写入一个关闭的通道ErrNoProgress
当许多对Read的调用都没有返回任何数据或错误时,通常是Reader实现失败的标志ErrShortBuffer
缓冲区过短ErrShortWrite
写入时接受的字节数比要求的少ErrUnexpectedEOF
在读取一个固定大小的块或数据结构的过程中遇到了EOFfunc Copy(dst Writer, src Reader) (written int64, err error)
复制func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)
缓冲复制func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
复制N个字节func Pipe() (*PipeReader, *PipeWriter)
创建一个内存中同步管道func ReadAll(r Reader) ([]byte, error)
读取所有func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)
至少读取min个字节func ReadFull(r Reader, buf []byte) (n int, err error)
读取buf长度func WriteString(w Writer, s string) (n int, err error)
将字符串s的内容写到w,w接受一个字节切片io.ioutil
可以看到io大部分都只是提供了一些接口,所以使用起来不是那么方便。所以标准库io.ioutil
提供了一些方便的操作函数。
函数 | 说明 |
---|---|
NopCloser(r io.Reader) io.ReadCloser | 返回一个包裹起给定 Reader r 的 ReadCloser , 这个 ReadCloser 带有一个无参数的 Close 方法 |
ReadAll(r io.Reader) ([]byte, error) | 对 r 进行读取, 直到发生错误或者遇到 EOF 为止, 然后返回被读取的数据 |
ReadDir(dirname string) ([]os.FileInfo, error) | 读取 dirname 指定的目录, 并返回一个根据文件名进行排序的目录节点列表 |
ReadFile(filename string) ([]byte, error) | 读取名字为 filename 的文件并返回文件中的内容 |
TempDir(dir, prefix string) (name string, err error) | 在目录 dir 中新创建一个带有指定前缀 prefix 的临时目录, 然后返回该目录的路径 |
TempFile(dir, prefix string) (f *os.File, err error) | 在目录 dir 新创建一个名字带有指定前缀 prefix 的临时文件, 以可读写的方式打开它, 并返回一个 *os.File 指针 |
WriteFile(filename string, data []byte, perm os.FileMode) error | 将给定的数据 data 写入到名字为 filename 的文件里面 |
bufio
很明显,bufio
比io
多了一个缓冲buf。那么,缓冲有何作用?
这是因为在一些很小的写入和读取时,很耗费io。 这时可以把这些小片段的数据放在缓冲中,然后再统一读取或写入,这样可以大大的提高效率。
bufio
中的Reader实现了io
中的接口。
type Reader struct {
buf []byte // 缓冲区,为字节切片
rd io.Reader // reader 由 client 提供
r, w int // 缓冲区读写的位置
err error
lastByte int // UnreadByte的最后读取的字节;-1表示无效
lastRuneSize int // UnreadRune最后读取的字符大小;-1表示无效
}
const ( defaultBufSize = 4096 //默认大小 ) const minReadBufferSize = 16 //最小尺寸 func NewReaderSize(rd io.Reader, size int) *Reader { // Is it already a Reader? b, ok := rd.(*Reader) if ok && len(b.buf) >= size { return b } if size < minReadBufferSize { //minReadBufferSize==16 size = minReadBufferSize } r := new(Reader) r.reset(make([]byte, size), rd) return r } // NewReader returns a new Reader whose buffer has the default size. func NewReader(rd io.Reader) *Reader { return NewReaderSize(rd, defaultBufSize) }
可以看到有两个方法NewReader()
和NewReaderSize()
可以创建bufio.Reader
。区别在于NewReader()
使用默认的尺寸(4096)来调用NewReaderSize()
来实现,而NewReaderSize()
则可以自己指定尺寸,注意最小值为16。
方法 | 作用 | 接口实现 |
---|---|---|
Buffered()int | 返回可从当前缓冲区读取的字节数 | |
Discard(n int) (discarded int, err error) | 跳过接下来的n个字节,返回被丢弃的字节数 | |
Peek(n int) ([]byte, error) | 返回下一个n个字节,但是不推进阅读器 | |
Read(p []byte) (n int, err error) | 读取数据到p中 | io.Reader |
ReadByte() (byte, error) | 读取并返回一个字节 | io.ByteReader |
ReadBytes(delim byte) ([]byte, error) | 读到输入中第一次出现delim为止,返回一个包含数据的切片,直到并包括分界符 | |
ReadLine() (line []byte, isPrefix bool, err error) | 一个低级别的读行原语 | |
ReadRune() (r rune, size int, err error) | 读取一个UTF-8编码的Unicode字符,并返回符文和它的大小(字节)。 | |
ReadSlice(delim byte) (line []byte, err error) | 读取直到输入中第一次出现delim,返回一个指向缓冲区中字节的切片 | |
ReadString(delim byte) (string, error) | 读取直到输入中第一次出现delim,返回一个包含数据的字符串,直到并包括分隔符 | |
Reset(r io.Reader) | 丢弃任何缓冲数据,重置所有状态,并将缓冲读卡器切换为从r读取 | |
Size() int | 返回底层缓冲区的大小(字节) | |
UnreadByte() error | 解读最后一个字节 | |
UnreadRune() error | 解读最后一个字符 | |
WriteTo(w io.Writer) (n int64, err error) | 实现WriteTo接口 | io.WriterTo |
type Writer struct {
err error //错误
buf []byte //缓冲区
n int
wr io.Writer
}
func NewWriterSize(w io.Writer, size int) *Writer { // Is it already a Writer? b, ok := w.(*Writer) if ok && len(b.buf) >= size { return b } if size <= 0 { size = defaultBufSize } return &Writer{ buf: make([]byte, size), wr: w, } } func NewWriter(w io.Writer) *Writer { return NewWriterSize(w, defaultBufSize) }
可以看到,类似于bufio.Reader
,bufio.Writer
也有两种方法,一种是默认大小的方式NewWriter
和可以指定尺寸的NewWriterSize
方式,而当尺寸小于0时为默认大小4096。
方法 | 作用 | 接口实现 |
---|---|---|
Available() int | 返回缓冲区内有多少个未使用的字节。 | |
Buffered() int | 返回已经写进当前缓冲区的字节数 | |
Flush() error | 将任何缓冲数据写入底层的io.Writer | |
ReadFrom(r io.Reader) (n int64, err error) | io.ReaderFrom的实现 | io.ReaderFrom |
Reset(w io.Writer) | 丢弃任何未刷新的缓冲数据,清除任何错误,并重置b,将其输出写入w | |
Size() int | 返回底层缓冲区的大小,单位是字节 | |
Write(p []byte) (nn int, err error) | 将p的内容写进缓冲区 | io.Writer |
WriteByte(c byte) error | 写一个字节 | io.ByteWriter |
WriteRune(r rune) (size int, err error) | 写一个字符 | |
WriteString(s string) (int, error) | 写一个字符串 | io.StringWriter |
扫描器模块的主要作用是把数据流分割成一个个标记并除去它们之间的空格
type Scanner struct {
r io.Reader // 由client提供的reader.
split SplitFunc // 分隔标记的函数.
maxTokenSize int // 标记的最大尺寸
token []byte // 由split返回的最后一个token
buf []byte // 用作分割参数的缓冲区
start int // buf中第一个未处理的字节
end int // 缓冲区内的数据结束
err error // 错误
empties int // 连续的空标记的数量
scanCalled bool // 扫描已被调用;缓冲区正在使用中
done bool // 扫描已完成
}
const (
MaxScanTokenSize = 64 * 1024
startBufSize = 4096 // Size of initial allocation for buffer.
)
func NewScanner(r io.Reader) *Scanner {
return &Scanner{
r: r,
split: ScanLines, //默认函数
maxTokenSize: MaxScanTokenSize, //64*4096
}
}
方法 | 作用 |
---|---|
Buffer(buf []byte, max int) | 设置扫描时使用的初始缓冲区和扫描时可能分配的最大缓冲区大小 |
Bytes() []byte | 返回调用Scan所产生的最近的token |
Err() error | 返回扫描器遇到的第一个非EOF错误 |
Scan() bool | 扫描将扫描器推进到下一个令牌,然后通过字节或文本方法获得令牌 |
Split(split SplitFunc) | 设置扫描器的分割功能 |
Text() string | 返回由调用Scan产生的最近的令牌,作为一个新分配的字符串,持有其字节数。 |
os
与文件进行交互是程序中常见的一个行为。在os
库中,实现了对与文件的io。
type File struct {
*file // os specific
}
/*file_unix.go*/
type file struct {
pfd poll.FD
name string // 名称
dirinfo *dirInfo // nil unless directory being read
nonblock bool // whether we set nonblocking mode
stdoutOrErr bool // whether this is stdout or stderr
appendMode bool // whether file is opened for appending
}
可以看到由于不同的操作系统的文件实现方式不一致,所以因操作系统而各异。
/*file_unix.go*/
// 给定文件描述符fd和文件名 返回一个新的文件
func NewFile(fd uintptr, name string) *File {
kind := kindNewFile
if nb, err := unix.IsNonblock(int(fd)); err == nil && nb {
kind = kindNonBlock
}
return newFile(fd, name, kind)
}
相关函数 | 功能 |
---|---|
Create(name string) (*File, error) | 创建或截断命名的文件 |
CreateTemp(dir, pattern string) (*File, error) | 在目录dir中创建一个新的临时文件,打开该文件进行读写,并返回结果文件 |
Open(name string) (*File, error) | 打开指定的文件进行阅读 |
OpenFile(name string, flag int, perm FileMode) (*File, error) | 通用的打开调用 |
方法 | 作用 | 接口实现 |
---|---|---|
Chdir() error | 改变工作目录 | |
Chmod(mode FileMode) error | 改变模式 | |
Chown(uid, gid int) error | 改变命名文件的数字UID和GID | |
Close() error | 关闭文件 | |
Fd() uintptr | 返回打开文件的描述符 | |
Name() string | 返回提交给Open的文件名 | |
Read(b []byte) (n int, err error) | | io.Reader` | ||
ReadAt(b []byte, off int64) (n int, err error) | io.ReaderAt | |
ReadDir(n int) ([]DirEntry, error) | 读取与文件f相关的目录的内容,并按目录顺序返回DirEntry值的一个片断 | |
ReadFrom(r io.Reader) (n int64, err error) | io.ReaderFrom | |
Readdir(n int) ([]FileInfo, error) | 读取与文件相关的目录的内容,并返回最多 N 个 FileInfo 值的片断,就像 Lstat 返回的那样,按目录顺序。 | |
Readdirnames(n int) (names []string, err error) | 读取与文件相关的目录的内容,并按目录顺序返回该目录中最多n个文件名的片断。 | |
Seek(offset int64, whence int) (ret int64, err error) | io.Seeker | |
SetDeadline(t time.Time) error | 设置一个文件的读写期限 | |
SetReadDeadline(t time.Time) error | 为未来的读取调用和任何当前被阻止的读取调用设定最后期限 | |
SetWriteDeadline(t time.Time) error | 为任何未来的写入调用和任何当前被阻断的写入调用设定了截止日期。 | |
Stat() (FileInfo, error) | 返回描述文件的FileInfo结构 | |
Sync() error | 将文件的当前内容提交到稳定存储区 | |
SyscallConn() (syscall.RawConn, error) | 返回一个原始文件 | |
Truncate(size int64) error | 改变文件的尺寸 | |
Write(b []byte) (n int, err error) | io.Writer | |
WriteAt(b []byte, off int64) (n int, err error) | io.WriterAt | |
WriteString(s string) (n int, err error) | io.StringWriter |
而对于标准输入输出stdin,stdout,stderr它们其实就是指定的File。
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
net
在net库中,定义了底层的接口Conn。
type Conn interface {
Read(b []byte) (n int, err error)
Write(b []byte) (n int, err error)
Close() error
LocalAddr() Addr
RemoteAddr() Addr
SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
}
同时也实现了一个接口类型conn
type conn struct {
fd *netFD
}
方法 | 接口实现 |
---|---|
Read(b []byte) (n int, err error) | io.Reader |
Write(b []byte) (n int, err error) | io.Writer |
Close() error | io.Closer |
strings
对字符串进行功效处理,在strings里面有一个Reader数据结构。
type Reader struct {
s string //字符串
i int64 // 当前读取下标
prevRune int // 先前读取字符的下标
}
方法 | 作用 | 接口实现 |
---|---|---|
Len() int | 返回未读部分的字节个数 | |
Read(b []byte) (n int, err error) | io.Reader | |
ReadAt(b []byte, off int64) (n int, err error) | io.ReaderA | |
ReadByte() (byte, error) | io.ByteReader | |
ReadRune() (ch rune, size int, err error) | io.RuneReader | |
Reset(s string) | 重置 | |
Seek(offset int64, whence int) (int64, error) | io.Seeker | |
Size() int64 | 返回底层字符串的原始长度 | |
UnreadByte() error | io.RuneScanner | |
WriteTo(w io.Writer) (n int64, err error) | io.WriterTo |
bytes
类似于strings
,byte
中也有一个Reader。它两大体类似,只是区别于一个s是string,一个是[]byte。篇幅有限,就不具体展开。
bytes.Buffer
是一个可变大小的字节缓冲区,可以读也可以写。
type Buffer struct {
buf []byte // 缓冲区 内容为buf[off : len(buf)]
off int // 读在 &buf[off], 写在 &buf[len(buf)]
lastRead readOp // 上一次读的操作
}
方法 | 作用 | 接口实现 |
---|---|---|
Bytes() []byte | 返回一个长度为b.Len()的片断,持有缓冲区的未读部分 | |
Cap() int | 返回缓冲区基础字节片的容量,即分配给缓冲区数据的总空间 | |
Grow(n int) | 增加容量 | |
Len() int | 返回未读部分的字节个数 | |
Next(n int) []byte | 从缓冲区返回一个包含下一个n个字节的片断,推进缓冲区,就像这些字节是由Read返回的一样 | |
Read(b []byte) (n int, err error) | io.Reader | |
ReadByte() (byte, error) | io.ByteReader | |
ReadFrom(r io.Reader) (n int64, err error) | io.ReaderFrom | |
ReadBytes(delim byte) (line []byte, err error) | 读取直到delim的字节 | |
ReadRune() (ch rune, size int, err error) | io.RuneReader | |
ReadString(delim byte) (line string, err error) | 读取直到delim的字符串 | |
Reset(s string) | 重置 | |
string() string | 将缓冲区未读部分的内容作为一个字符串返回。 | |
Truncate(n int) | 丢弃缓冲区中除前n个未读字节外的所有字节,但继续使用相同的分配存储空间。 | |
UnreadByte() error | io.ByteScanner | |
UnreadRune() error | io.RuneScanner | |
Write(p []byte) (n int, err error) | io.Writer | |
WriteByte(c byte) error | io.ByteWriter | |
WriteRune(r rune) (n int, err error) | 将Unicode代码点r的UTF-8编码追加到缓冲区。 | |
WriteString(s string) (n int, err error) | io.StringWriter | |
WriteTo(w io.Writer) (n int64, err error) | io.WriterTo |
io
是Go中输入输出处理的最基本的库,它定义了许多相关接口io
大部分是定义的接口,使用起来不太实用。io.ioutil
提供了许多方便的实用函数io
中的接口
bufio
实现了带缓冲的ioos
实现了文件ionet
实现了网络iostrings
实现了字符串处理iobytes
实现了字节处理ioCopyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。