一文详解golang通过io包进行文件读写
来源:脚本之家
时间:2023-05-13 09:42:37 363浏览 收藏
欢迎各位小伙伴来到golang学习网,相聚于此都是缘哈哈哈!今天我给大家带来《一文详解golang通过io包进行文件读写》,这篇文章主要讲到读写、包、文件、IO等等知识,如果你对Golang相关的知识非常感兴趣或者正在自学,都可以关注我,我会持续更新相关文章!当然,有什么建议也欢迎在评论留言提出!一起学习!
在 golang
语言中,实现io的读与写,只要实现了如下的读写接口即可:
// go 1.19/src/io/io.go type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) }
golang
语言自身提供了很多实现这两个接口的结构体,比较典型的有:io、os、bytes.buffer、strings、bufio
等,这些解决了文件的读写,字节、字符串的读写,或者是带缓冲的读写等。
今天我们先来看看 io包
中提供的这些接口,简单了解下相关原理。
1.io包的读写函数
典型的相关函数声明如下:
// 读操作 func ReadAll(r Reader) ([]byte, error) {...} // 写操作 func Copy(dst Writer, src Reader) (written int64, err error) {...} func WriteString(w Writer, s string) (n int, err error) {...}
在上面的 ReadAll()
函数中,接收一个 Reader
类型的参数,比如 os.File
类型,又或者是其他的实现了 io.Reader
接口的结构体类型,输出读取到的内容,以字节数组形式输出,外附一个 error
错误,我们进一步看看其内部实现:
func ReadAll(r Reader) ([]byte, error) { // 新建字节数组,cap=512 b := make([]byte, 0, 512) // 开启循环读取内容 for { // if len(b) == cap(b) { // Add more capacity (let append pick how much). b = append(b, 0)[:len(b)] } // 读取 reader中的内容,填充到对应部分 n, err := r.Read(b[len(b):cap(b)]) b = b[:len(b)+n] // 如果读到了结尾,可以返回数据 if err != nil { if err == EOF { err = nil } return b, err } } }
从上面源码可以知道,io.ReadAll()
函数,通过一次读取 Reader
中的所有内容,如果是小文件无所谓,占用内存有限,但如果是好几个G的文件呢,是不是,本来服务器内存有限,以下占用几G内存,这样读取不太合理,当然,golang
也为我们提供了带缓冲的读取,这是后话,后面再讨论。
接下来看看写入操作的源码实现:
// io.Copy() func Copy(dst Writer, src Reader) (written int64, err error) { return copyBuffer(dst, src, nil) // 实际通过调用此函数实现 } func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { if buf != nil && len(buf) == 0 { // 当buf非空或者长度0,panic panic("empty buffer in CopyBuffer") } return copyBuffer(dst, src, buf) // 继续往下调用 } func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { // If the reader has a WriteTo method, use it to do the copy. // Avoids an allocation and a copy. if wt, ok := src.(WriterTo); ok { // 源实现了writeTo就可以直接调用结束 return wt.WriteTo(dst) } // Similarly, if the writer has a ReadFrom method, use it to do the copy. if rt, ok := dst.(ReaderFrom); ok { // 目标实现了readFrom,也可以直接调用结束 return rt.ReadFrom(src) } if buf == nil { size := 32 * 1024 if l, ok := src.(*LimitedReader); ok && int64(size) > l.N { if l.N 0 { nw, ew := dst.Write(buf[0:nr]) // 目标w从buf中写入内容 if nw
通过源码走读,我们可以看到,在函数中传入 Writer
作为目标写入对象,传入 Reader
作为源读取对象,如果 Reader
对象实现 WriteTo
方法,我们可以直接调用此完成 copy
, 或者是 Writer
对象实现了 ReadFrom
方法,我们可以直接调用此完成 copy
,都没有实现,就只有通过 buf
作为中转,通过循环,先从源读取内容,再写入目标对象中,最后返回整个内容的长度,当然其中也有相关错误处理,这里不做讨论。
接下来 io包
中提供的另外的写入操作实现:
// io.WriteString() func WriteString(w Writer, s string) (n int, err error) { if sw, ok := w.(StringWriter); ok { // 如w实现了 StringWriter,则调用相关方法实现写入 return sw.WriteString(s) } return w.Write([]byte(s)) // 作为兜底,w对象是肯定实现了 Write() 方法,所以调用此方法实现写入 }
从上面代码可以看到,该实现逻辑简单,具体见注释。
io包实现读写操作
准备:
- file.txt,作为读取源,然后再写入其他文件中 写操作,我们实现了2种,一种
io.Copy()
,一种io.WriteString()
,具体使用见下面代码:
func ioRW() { // read f, err := os.Open("D:\\demo1\\src\\demo23\\go-io\\file\\file.txt") defer f.Close() if err != nil { fmt.Printf("err: %s\n", err) return } b, err := io.ReadAll(f) if err != nil { fmt.Printf("err: %s\n", err) return } fmt.Printf("ioRW read content: \n%s\n", b) // write // 1.io.Copy() fw, err := os.Create("io_w.txt") defer fw.Close() n, err := io.Copy(fw, strings.NewReader(string(b))) if err != nil { fmt.Printf("err: %s\n", err) return } // 2.io.WriteString() fw_1, _ := os.Create("io_w_1.txt") defer fw_1.Close() n, err := io.WriteString(fw_1, string(b)) if err != nil { fmt.Printf("err: %s\n", err) return } fmt.Printf("ioRW write size: %d\n", n) }
2.io.ioutil包实现读写操作
所谓 util
,肯定是作为工具使用,怎么方便怎么来,我们甚至通过文件名就可以完成操作,具体实现细节我们不关心,总之它实现了这些功能。
注意
由于我使用的 go 1.19
,在源码中,明确声明在 go 1.16
后,逐渐弃用了,所以使用高版本的 go
时要注意这个问题,这里为了增加源码的了解熟悉,也拿出来作为分享。
读操作
// Deprecated: As of Go 1.16, this function simply calls io.ReadAll. func ReadAll(r io.Reader) ([]byte, error) { return io.ReadAll(r) } // Deprecated: As of Go 1.16, this function simply calls os.ReadFile. func ReadFile(filename string) ([]byte, error) { return os.ReadFile(filename) }
在 ReadAll
中,实际也是调用上面的 io.ReadAll()
,这里不赘述,在 ReadFile
中,我们传入个文件名,就可以读取到整个字节内容,实际它也是调用 os.ReadFile()
实现,后面再讨论 os包
。
写操作
// Deprecated: As of Go 1.16, this function simply calls os.WriteFile. func WriteFile(filename string, data []byte, perm fs.FileMode) error { return os.WriteFile(filename, data, perm) }
可以看到,这里也是调用了 os.WriteFile()
实现文件的写入,只需要我们传入待写入的文件名,写入的字节数组,以及写入文件的权限,是不是很简单。
代码实用
func ioutilRW() { // read b, err := ioutil.ReadFile("D:\\demo1\\src\\demo23\\go-io\\file\\file.txt") if err != nil { fmt.Printf("err: %s\n", err) return } fmt.Printf("ioutilRW read content: \n%s\n", b) // write err = ioutil.WriteFile("ioutilRW_w.txt", b, 664) if err != nil { fmt.Printf("err: %s\n", err) return } fmt.Println("ioutilRW write err: ", err) }
上面的读取与写入操作实际很简单,代码操作没难度,相信你一看就懂。
今天我们通过 io.Reader/io.Writer
接口了解了实现了该接口的都可以作为相关对象,通过 io包
也实现了文件的读写,后面我们会陆续分享基于 os/bufio/bytes/strings
的读写操作,毕竟 IO
范围还是很广,文件IO
只是一部分。
理论要掌握,实操不能落!以上关于《一文详解golang通过io包进行文件读写》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!
-
140 收藏
-
164 收藏
-
208 收藏
-
400 收藏
-
235 收藏
-
233 收藏
-
322 收藏
-
181 收藏
-
316 收藏
-
244 收藏
-
300 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习