Go 语言中,为了方便开发者使用,将 I/O 操作封装在了大概如下几个包中:
- io 为 I/O 原语(I/O primitives)提供基本的接口
- io/ioutil 封装一些实用的 I/O 函数
- fmt 实现格式化 I/O,类似 C 语言中的 printf 和 scanf ,后面会详细讲解
- bufio 实现带缓冲I/O
在 io 包中最重要的是两个接口:Reader 和 Writer 接口。这两个接口是我们了解整个I/O的关键,我们只要记住:实现了这两个接口,就有了 I/O 的功能 。
有关缓冲:
- 内核中的缓冲:无论进程是否提供缓冲,内核都是提供缓冲的,系统对磁盘的读写都会提供一个缓冲(内核高速缓冲),将数据写入到块缓冲进行排队,当块缓冲达到一定的量时,才把数据写入磁盘。
- 进程中的缓冲:是指对输入输出流进行了改进,提供了一个流缓冲,当调用一个函数向磁盘写数据时,先把数据写入缓冲区,当达到某个条件,如流缓冲满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲中,再经块缓冲写入磁盘。
Go 语言提供了很多读写文件的方式,一般来说常用的有三种。 一:os.File 实现了Reader 和 Writer 接口,所以在文件对象上,我们可以直接读写文件。
func (f *File) Read(b []byte) (n int, err error)
func (f *File) Write(b []byte) (n int, err error)
在使用File.Read读文件时,可考虑使用buffer:
package main
import (
"fmt"
"os"
)
func main() {
b := make([]byte, 1024)
f, err := os.Open("./tt.txt")
_, err = f.Read(b)
f.Close()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(b))
}
二:ioutil库,没有直接实现Reader 和 Writer 接口,但是通过内部调用,也可读写文件内容:
func ReadAll(r io.Reader) ([]byte, error)
func ReadFile(filename string) ([]byte, error) //os.Open
func WriteFile(filename string, data []byte, perm os.FileMode) error //os.OpenFile
func ReadDir(dirname string) ([]os.FileInfo, error) // os.Open
三:使用bufio库,这个库实现了I/O的缓冲操作,通过内嵌io.Reader、io.Writer接口,新建了Reader ,Writer 结构体。同时也实现了Reader 和 Writer 接口。
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
lastRuneSize int
}
type Writer struct {
err error
buf []byte
n int
wr io.Writer
}
func (b *Reader) Read(p []byte) (n int, err error)
func (b *Writer) Write(p []byte) (nn int, err error)
这三种读方式的效率怎么样呢,我们可以看看:
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
"time"
)
func read1(path string) {
fi, err := os.Open(path)
if err != nil {
panic(err)
}
defer fi.Close()
buf := make([]byte, 1024)
for {
n, err := fi.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if 0 == n {
break
}
}
}
func read2(path string) {
fi, err := os.Open(path)
if err != nil {
panic(err)
}
defer fi.Close()
r := bufio.NewReader(fi)
buf := make([]byte, 1024)
for {
n, err := r.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if 0 == n {
break
}
}
}
func read3(path string) {
fi, err := os.Open(path)
if err != nil {
panic(err)
}
defer fi.Close()
_, err = ioutil.ReadAll(fi)
}
func main() {
file := "" //找一个大的文件,如日志文件
start := time.Now()
read1(file)
t1 := time.Now()
fmt.Printf("Cost time %v\n", t1.Sub(start))
read2(file)
t2 := time.Now()
fmt.Printf("Cost time %v\n", t2.Sub(t1))
read3(file)
t3 := time.Now()
fmt.Printf("Cost time %v\n", t3.Sub(t2))
}
经过多次测试,基本上保持 file.Read > ioutil >bufio 这样的成绩, bufio读同一文件耗费时间最少,效果稳稳地保持在最佳。