李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
GO
正文
28.Golang读取文件
Leefs
2022-07-24 PM
1652℃
0条
[TOC] ### 前言 ![28.Golang读取文件01.png](https://lilinchao.com/usr/uploads/2022/07/2974068165.png) ### 一、概念 文件是数据源(保存数据的地方)的一种,文件最主要的作用就是保存数据。 文件在程序中是以流的形式来操作的。 + **输入流和输出流** ![28.Golang读取文件02.jpg](https://lilinchao.com/usr/uploads/2022/07/3522567276.jpg) + **流**:数据在数据源(文件)和程序(内存)之间经历的路径 + **输入流**:数据从数据源(文件)到程序(内存)的路径 + **输出流**:数据从程序(内存)到数据源(文件)的路径 ### 二、读取文件操作 #### 2.1 打开和关闭文件 + **打开文件** ```go func Open(filename string) (file *File, err error) ``` Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有`O_RDONLY`模式。如果出错,错误底层类型是`*PathError`。 + **关闭文件** ```go func (f *File) Close() error ``` Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。 **示例** ```go package main import ( "fmt" "os" ) func main() { //只读方式打开当前目录下的test.txt file, err := os.Open("test.txt") if err != nil { fmt.Println("open file failed!,err:",err) } //返回的是一个指针 fmt.Println(&file) //关闭文件 //err = file.Close() //if err != nil{ // fmt.Println("close file failed!,err:",err) //} //为了防止文件忘记关闭,通常使用defer注册文件关闭语句。 defer file.Close() // 关闭文件 } ``` **运行结果** ``` 0xc0000ce018 ``` ##### defer 语句 + `defer`—般用于资源的释放和异常的捕捉。 + `defer`语句会将其后面跟随的语句进行`延迟处理`;跟在`defer`后面的语言将会在程序进行最后的`return`之后再执行。 + 在`defer`归属的函数即将返回时,将延迟处理的语句按`defer`的逆序进行执行,也就是说,先被`defer`的语句最后被执行,最后被 `defer`的语句,最先被执行。 #### 2.2 file.Read() 读取文件 + ##### **Read 方法定义** ```go func (f *File) Read(b []byte) (n int, err error) ``` 从文件对象中读取长度为b的字节,返回当前读到的字节数以及错误信息。因此使用该方法需要先初始化一个符合内容大小的空的字节列表。读取到文件的末尾时,该方法返回0,`io.EOF`。 + ##### **ReadAt方法定义** ```go func (file *File) ReadAt(b []byte, off int64) (n int, err Error) ``` + **b**:是指定字节长度的缓冲区 + **off**:int64类型的偏移量,从此位置开始读取。 从文件的off偏移量开始读取长度为b的字节。返回读取到字节数以及错误信息。当读取到的字节数n小于想要读取字节的长度`len(b)`的时候,该方法将返回非空的error。当读到文件末尾时,err返回io.EOF。 *注意:ReadAt 绝对不允许出现,没有读满 buffer,又非 EOF,又没有 err 的情况发生,这个是接口语义明确规定的,这是一个非常细节的区别。* + ##### **一次性读取** 适用于读取较小文件使用 ```go package main import ( "fmt" "io" "os" ) func main() { //1、只读方式打开当前目录下的test2.txt file, err := os.Open("test2.txt") if err != nil { fmt.Println("open file failed!,err:",err) return } //3、当函数退出时,及时关闭file //使用 defer 内置函数 当函数退出时才会调用,要及时关闭否则会内存泄露 defer file.Close() //2、使用Read方法读取数据,注意一次只会读取128个字节 tmp := make([]byte, 128) n, err := file.Read(tmp) //使用ReadAt方法读取数据,注意一次只会读取6个字节 //tmp := make([]byte, 6) //n, err := file.ReadAt(tmp,6) //io.EOF 表示文件的末尾 if err == io.EOF { fmt.Println("文件读取完毕") return } if err != nil { fmt.Println("read file failed,err:",err) return } fmt.Printf("读取了 %d 字节数据\n", n) fmt.Println(string(tmp[:n])) } ``` **运行结果** ``` 读取了 13 字节数据 Hello Golang! ``` + ##### **循环读取** 使用 for 循环读取文件中的所有数据 ```go package main import ( "fmt" "io" "os" ) //循环读取文件 func main() { //只读方式打开当前目录下的test.txt file, err := os.Open("test.txt") if err != nil { fmt.Println("open file failed!,err:",err) return } //关闭文件 defer file.Close() //循环读取文件 var content []byte //使用Read方法读取数据,注意一次只会读取128个字节 tmp := make([]byte, 128) for { n, err := file.Read(tmp) //每次读取128个字节 if err == io.EOF { fmt.Println("文件读取完毕") break } if err != nil { fmt.Println("read file failed,err:",err) return } //每次读取的内容都追加到已知的byte切片中 content = append(content,tmp[:n]...) } //将byte类型转换结果,打印结果 fmt.Println(string(content)) } ``` **运行结果** ``` 文件读取完毕 水陆草木之花,可爱者甚蕃。晋陶渊明独爱菊。自李唐来,世人甚爱牡丹。 予独爱莲之出淤泥而不染,濯清涟而不妖,中通外直,不蔓不枝,香远益清,亭亭净植,可远观而不可亵玩焉。 予谓菊,花之隐逸者也;牡丹,花之富贵者也;莲,花之君子者也。 噫!菊之爱,陶后鲜有闻。莲之爱,同予者何人?牡丹之爱,宜乎众矣! ``` **说明** 这里的循环读取文件其实就是一个不断追加的过程,将每次读取的128个字节追加到预先定义好的content切片中,最后将切片转换成string类型,进行打印显示。 #### 2.3 bufio 读取文件 + **语法** ```go //bufio.NewReader(rd io.Reader) *Reader r := bufio.NewReader(file) //func (b *Reader) ReadString(delim byte) (string, error) n, err := r.Read(buf) ``` **参数** + **file**:要读取的文件句柄; + **buf**:读取的数据存放的缓冲区。 **返回值** + **n**:读取到的长度 + **err**:读取失败,则返回错误信息。 使用 NewReader 读取文件时,首先,需要打开文件,接着, 使用打开的文件返回的文件句柄当作 **函数参数** 传入 NewReader。 最后,使用 NewReader 返回的 reader 对象调用 Read 来读取文件。文件读取结束的标志是返回的 n 等于 0,因此,如果需要读取整个文件内容,那么我们需要使用 **for 循环** 不停的读取文件,直到 n 等于 0。 **示例** ```go package main import ( "bufio" "fmt" "io" "os" ) //bufio读取文件 func main() { //只读方式打开当前目录下的test.txt file, err := os.Open("test.txt") if err != nil { fmt.Println("open file failed!,err:",err) return } //关闭文件,避免内存泄露 defer file.Close() //通过bufio缓冲区读取文件 reader := bufio.NewReader(file) //建立缓冲区,将文件内容放入到缓冲区 //循环读取文件信息 for { line, err := reader.ReadString('\n') //读到一个换行就结束 if err == io.EOF { //io.EOF 表示文件的末尾 //输出最后的内容 if len(line) != 0 { fmt.Println(line) } fmt.Println("文件读取完毕") break } if err != nil { fmt.Println("read file failed,err:",err) return } fmt.Println(line) } } ``` **运行结果** ```go 水陆草木之花,可爱者甚蕃。晋陶渊明独爱菊。自李唐来,世人甚爱牡丹。 予独爱莲之出淤泥而不染,濯清涟而不妖,中通外直,不蔓不枝,香远益清,亭亭净植,可远观而不可亵玩焉。 予谓菊,花之隐逸者也;牡丹,花之富贵者也;莲,花之君子者也。 噫!菊之爱,陶后鲜有闻。莲之爱,同予者何人?牡丹之爱,宜乎众矣! 文件读取完毕 ``` #### 2.4 ioutil 读取文件 **语法** ```go func ReadFile(name string) ([]byte, error) ``` + **name**:文件路径地址 使用 `io/ioutil.ReadFile` 方法一次性将文件读取到内存中,只需要将文件名作为参数传入。 **示例** ```go package main import ( "fmt" "io/ioutil" ) //ioutil 读取整个文件 func main() { content, err := ioutil.ReadFile("test.txt") if err != nil { fmt.Println("read failed,err:",err) return } fmt.Println(string(content)) } ``` **运行结果** ``` 水陆草木之花,可爱者甚蕃。晋陶渊明独爱菊。自李唐来,世人甚爱牡丹。 予独爱莲之出淤泥而不染,濯清涟而不妖,中通外直,不蔓不枝,香远益清,亭亭净植,可远观而不可亵玩焉。 予谓菊,花之隐逸者也;牡丹,花之富贵者也;莲,花之君子者也。 噫!菊之爱,陶后鲜有闻。莲之爱,同予者何人?牡丹之爱,宜乎众矣! ``` *注意:如果文件比较大,一次性读取整个文件会占用很大的内存,影响执行效率。* *建议读取小文件时使用,不太适用于大文件的读取。* ### 三、效率比较 - **当文件较小(KB 级别)时,ioutil > bufio > file.Read()。** - **当文件大小比较常规(MB 级别)时,三者差别不大,但 bufio 优势已经显现出来。** - **当文件较大(GB 级别)时,bufio > file.Read()> ioutil。** **总结** 当读取小文件时,使用`ioutil`效率明显优于`file.Read()`和`bufio`,但如果是大文件,`bufio`读取会更快,效率更高。 *附参考文章链接* *https://www.cnblogs.com/KylinBlog/p/13607284.html* https://blog.csdn.net/weixin_28881575/article/details/112571155
标签:
Golang基础
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2263.html
上一篇
27.Golang之反射三定律
下一篇
29.Golang写入文件
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
4
标签云
队列
数据结构和算法
Spark SQL
哈希表
序列化和反序列化
Jenkins
Shiro
随笔
并发编程
SpringBoot
Netty
Python
ClickHouse
SpringCloud
Golang
设计模式
Java
NIO
Spring
算法
稀疏数组
HDFS
JavaScript
Redis
Http
人工智能
SQL练习题
链表
工具
Thymeleaf
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭