CloudGeek讲golang系列 - golang中的io操作
0、写在最前面
这篇文章讲什么?不讲什么?
我会从什么是io操作开始,逐步展开golang中的io知识体系;这个过程不会去按顺序介绍io包的所有接口啥的,那样做看似全面,其实很枯燥,看完就是忘完。如果你对IO操作有基础的认识,但是不知道golang中的io是怎么回事,那么下面的介绍会帮助你轻松入门了解io in go!
一、何为IO操作
io操作简单理解就是从某处读/写到某处;比如读取键盘输入,读取文件,输出到终端,输出到一个文件……
二、golang中的io包
go语言的io 包提供了I/O原语的基本接口,我们先看最最基础的2个接口
- io.Reader
- io.Writer
如上所示,2个接口的定义很简单,都只包含一个方法,下面分别介绍
1、Read(p []byte) (n int, err error)
- Read方法有一个[]byte类型的参数p,p相当于存放读取到的数据流的载体,p的长度可以大于实际接收到的数据,也就是容量为10的p可以只存放大小为5的数据,没有要求放满。
- 当 Read 在成功读取 n > 0 个字节后遇到一个错误或EOF,它就会返回读取的字节数,此时err是nil或者EOF。
Read的用法应该还是容易理解,就是用一个p来暂存读取到的数据。任意一个类型只要实现了Read方法就算实现了Reader接口,也就成为了Reader接口的一个实例。如果暂时没有完全理解,等后面看完具体的用例再回过头来看看Read方法吧~
2、Write(p []byte) (n int, err error)
Write 将 len(p) 个字节的数据写入到基本数据流中。它返回从 p 中被写入的字节数 n(0 <= n <= len(p))以及任何遇到的引起写入提前停止的错误。若 Write 返回的 n < len(p),它就必须返回一个非nil的错误。也就是说p里应该全部是需要output的数据,全部完成写操作了就返回len(p)和nil,如果len(p)>n,也就是写中断了,这时候err必须有错误信息。
下面先看一下标准库中实现了io.Writer和io.Reader接口的那些类型是怎么工作的
三、golang中的文件io
1、os.File
先看源码中怎么定义File的
注释是说File表示一个打开文件的描述符
File类型实现了十几个接口,其中包括了上面提到的io.Reader和io.Writer
先看一个简单的File读写文件操作(为了简化代码,突出重点,略去了错误处理等非功能代码)
- 读取文件内容
func main() { //打开一个文件,文件里面存的是数字123 f, _ := os.Open("d:\\1.txt") defer f.Close() //用一个长度为5的byte切片来读取数据 b := make([]byte, 5) //n也就是读取到的数据长度 n, _ := f.Read(b) //输出内容是:3 [49 50 51 0 0] 123 fmt.Println(n, b, string(b)) }
- 数据写入文件
func main() { //创建文件 f, _ := os.Create("d:\\666.txt") //待写入的数据 b := []byte("CloudGeek") //执行写操作,n为写入的字符数 n, _ := f.Write(b) //输出结果是:9 fmt.Println(n) }
上述代码很简短,实际文件读取操作的时候也不会这样用,不然就low了;这里只是为了给大家展示os.File类型实现了io.Writer和io.Reader接口,所以对应的Read和Write方法可以实现io操作,参数列表和返回值含义都是和前面介绍接口的时候讲的一样。
2、io/ioutil
- 例子一:读取整个文件
func main() {
b, err := ioutil.ReadFile("d:\\1.txt")
if err == nil {
fmt.Println(string(b))
}
}
- 例子二:写入数据到文件
func main() { //文件不存在会新建,权限通过perm指定,文件存在会被清空后再写入数据 err := ioutil.WriteFile("d:\\3.txt", []byte("CloudGeek"), 0666) if err != nil { fmt.Println(err) } }
- 例子三:目录遍历
func main() { //目录遍历 files, err := ioutil.ReadDir("d:\\d") if err != nil { fmt.Println(err) } else { for _, file := range files { fmt.Println("文件名:", file.Name(), "文件大小:", file.Size()) } } }
- 例子四:读取任意实现了io.Reader接口的数据到一个[]byte中
func main() { //file是os.File类型,实现了io.Reader接口 file, err := os.Open("d:\\d\\1.txt") if err != nil { fmt.Println(err) } else { //ReadAll方法接收io.Reader类型的参数,返回一个[]byte类型的结果 b, err := ioutil.ReadAll(file) if err != nil { fmt.Println(err) } else { //[]byte类型的数据转换为string后输出 fmt.Println(string(b)) } } }
3、bufio
bufio 包实现了带缓存的 I/O 操作,封装了io.Writer和io.Reader类型
- 例子一:带缓存的写字符串
func main() { //创建文件(忽略错误处理过程) file, _ := os.Create("d:\\d\\1.txt") defer file.Close() //写入2个字符串到缓存区 w := bufio.NewWriter(file) n1, _ := w.WriteString("CloudGeek!") n2, _ := w.WriteString("CloudGeek!") //将缓存中内容写到文件 w.Flush() fmt.Println(n1, n2) }
这个例子中bufio.NewWriter返回的是bufio.Writer类型,这个类型封装了io.Writer,增加了buf等字段来支持缓存操作,定义如下:
四、golang中读取用户键盘输入
先看一个及其简单的读取单个字符串输入的例子:
func main() { var username string fmt.Scanln(&username) fmt.Println("username is:", username) }
在这个例子中,在控制台输入CloudGeek,得到的输出结果是:username is: CloudGeek
Scanln读取到换行或者EOF就会结束,中间读取到的字符串不能包含空格,不然就处理为多个字符串,这个例子中只用了一个username变量接收,所以如果输入:cloud geek,就只能获取到cloud。Scanln方法是从os.Stdin读取数据,Stdin是什么呢?如下:
NewFile的返回值是*File类型的变量,File类型实现了io.Writer和io.Reader接口,所以又和开始介绍的io包对应起来了。
本文主要带你了解golang中的IO,最主要是掌握io接口Writer和Reader,知道这个的基础上,在具体应用的时候无论是用到文件操作,缓存处理,终端输入输出等哪一块,再去查查相关用法的时候应该容易理解的多!