golang逐行读取文件的操作
作者:思维的深度 发布时间:2023-07-10 14:39:56
我就废话不多说了,大家还是直接看代码吧~
func ReadLine(fileName string) ([]string,error){
f, err := os.Open(fileName)
if err != nil {
return nil,err
}
buf := bufio.NewReader(f)
var result []string
for {
line, err := buf.ReadString('\n')
line = strings.TrimSpace(line)
if err != nil {
if err == io.EOF { //读取结束,会报EOF
return result,nil
}
return nil,err
}
result = append(result,line)
}
return result,nil
}
补充:Golang读取文件和处理超大文件方案
Golang 操作文件的读取的方法很多,适用的场景也是各不相同,在此我们将文件的读取分为如下几种 :
文件整体读取
文件分片读取(块级读取)
文件行级读取
系统的配置不同,执行的耗时也不相同,此处给出一参考
系统配置 :
OS : Windows10
Memory : 16G
CPU (英特尔)Intel® Core™ i3-4370 CPU @ 3.80GHz(3800 MHz)
1. 文件整体读取
文件整体读取就是将文件一次性读取到,理解上是将文件的内容第一次就读取完了
使用场景 :
针对小文件比较合适(大文件读取空间和时间的消耗也很大)
对于整体性强的文件也比较合适(文件也不能太大)
代码示例1
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`
// 将整个文件都读取
func readAll(filePath string) {
start1 := time.Now()
ioutil.ReadFile(filePath)
fmt.Println("readAll spend : ", time.Now().Sub(start1))
}
func main() {
readAll(m11)
readAll(m400)
}
$ go run main.go
readAll spend : 6.9999ms
readAll spend : 358.8014ms
代码示例2
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`
// 将文件完整读取
func readAllBuff(filePath string) {
start1 := time.Now()
// 打开文件
FileHandle, err := os.Open(filePath)
if err != nil {
log.Println(err)
return
}
// 关闭文件
defer FileHandle.Close()
// 获取文件当前信息
fileInfo, err := FileHandle.Stat()
if err != nil {
log.Println(err)
return
}
buffer := make([]byte, fileInfo.Size())
// 读取文件内容,并写入buffer中
n, err := FileHandle.Read(buffer)
if err != nil {
log.Println(err)
}
// 打印所有切片中的内容
fmt.Println(string(buffer[:n]))
fmt.Println("readAllBuff spend : ", time.Now().Sub(start1))
}
func main() {
readAllBuff(m11)
readAllBuff(m400)
}
2. 文件分片读取
对文件一部分一部分逐步的读取,直到文件完全读取完
PS : 每次读取文件的大小是根据设置的 分片 大小 ,所以对于读取文本类型的文件时(例如 : 日志文件)
不一定是按照你的期望逐行输出,因为不会处理文本尾部的换行符,而是按照分片大小读取内容
使用场景 :
读取超大的文件很合适
读二进制类型的文件很合适(比如:音视频文件或者资源类型文件等)
代码示例
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`
// 文件一块一块的读取
func readBlock(filePath string) {
start1 := time.Now()
FileHandle, err := os.Open(filePath)
if err != nil {
log.Println(err)
return
}
defer FileHandle.Close()
// 设置每次读取字节数
buffer := make([]byte, 1024)
for {
n, err := FileHandle.Read(buffer)
// 控制条件,根据实际调整
if err != nil && err != io.EOF {
log.Println(err)
}
if n == 0 {
break
}
// 如下代码打印出每次读取的文件块(字节数)
//fmt.Println(string(buffer[:n]))
}
fmt.Println("readBolck spend : ", time.Now().Sub(start1))
}
func main() {
readBlock(m11)
readBlock(m400)
}
$ go run main.go
readBolck spend : 31.9814ms
readBolck spend : 1.0889488s
3. 文件逐行读取
对文件一行一行的读取,直到读到文件末尾
使用场景 :
读取超大的文件很合适(例如 : 超大log文件等)
读取的文件最好是有换行的(如果使用单行文件组成的大文件,需要注意)
对需要分析内容的大文件
统计某些数据出现的次数
查询某些数据是否存在
查找指定行的数据
示例代码1
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`
// 读取文件的每一行
func readEachLineReader(filePath string) {
start1 := time.Now()
FileHandle, err := os.Open(filePath)
if err != nil {
log.Println(err)
return
}
defer FileHandle.Close()
lineReader := bufio.NewReader(FileHandle)
for {
// 相同使用场景下可以采用的方法
// func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
// func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
// func (b *Reader) ReadString(delim byte) (line string, err error)
line, _, err := lineReader.ReadLine()
if err == io.EOF {
break
}
// 如下是某些业务逻辑操作
// 如下代码打印每次读取的文件行内容
fmt.Println(string(line))
}
fmt.Println("readEachLineReader spend : ", time.Now().Sub(start1))
}
func main(){
readEachLineReader(m11)
readEachLineReader(m400)
}
$ go run main.go
readEachLineReader spend : 16.9902ms
readEachLineReader spend : 537.9683ms
代码示例2
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`
// 读取文件的每一行
func readEachLineScanner(filePath string) {
start1 := time.Now()
FileHandle, err := os.Open(filePath)
if err != nil {
log.Println(err)
return
}
defer FileHandle.Close()
lineScanner := bufio.NewScanner(FileHandle)
for lineScanner.Scan() {
// 相同使用场景下可以使用如下方法
// func (s *Scanner) Bytes() []byte
// func (s *Scanner) Text() string
// 实际逻辑 : 对读取的内容进行某些业务操作
// 如下代码打印每次读取的文件行内容
fmt.Println(lineScanner.Text())
}
fmt.Println("readEachLineScanner spend : ", time.Now().Sub(start1))
}
func main() {
readEachLineScanner(m11)
readEachLineScanner(m400)
}
$ go run main.go
readEachLineScanner spend : 17.9895ms
readEachLineScanner spend : 574.1722ms
4. 总结
面试中常见的类似超大文件读取的问题,通常我们采用分片读取或者逐行读取的方案即可
大文件的上传也可以采用类似的解决方案 , 每次读取文件的部分内容上传(写入)网络接口中,直至文件读取完毕
普通的小文件并且对内容没有太多操作的,可以采用整体读取,速度相对较快
对文件内容有操作的采用分片读取和逐行读取更合适
二进制类型文件采用分片读取或者整体读取的方案比较合适
文件读取不仅是本地文件,要读去网络上的文件(各种文档,音视频,图片,和其他各种类型文件)时要访问到文件获取 io.ReadCloser 或者 io.Reader 后可以采用三种方式将文件内容读取到
func ReadAll(r io.Reader) ([]byte, error) 文件完整读取
func Copy(dst Writer, src Reader) (written int64, err error) 文件读取并写入
type Reader interface {
Read(p []byte) (n int, err error)
}
通过Reader 接口的 Read 方法读取
以上为个人经验,希望能给大家一个参考
来源:https://skaygo.blog.csdn.net/article/details/79269649
猜你喜欢
- 两个并发事务同时访问数据库表相同的行时,可能存在以下三个问题:1、幻想读:事务T1读取一条指定where条件的语句,返回结果集。此时事务T2
- 在用户登录windows操作系统的时候,如果触发到了登录表单的密码录入框上,并且此时按下了“大写锁定键(Caps Lock)”,那么界面上会
- 《Patterns for Sign Up &Ramp Up》很早就读完了,之所以今天才写读后感,细细读完了发现,它更为接近吸引注册
- 我们可以利用Session对象来进行注册验证。Session对象会帮我们把某一用户的信息保留下来,让后续的网页读取。我们就可以在用户注册成功
- 如何实现在下拉菜单里输入文字? 用这个代码试试看,应该可以的:<script>function pp(){se.opt
- 前几天,Opera宣布其用户已经超过1亿——桌面版和手机版均超过5000万。Opera Mini是一个很优秀的手机浏览器,对手机用户而言,O
- 导航标签彼此互斥、完全穷尽。导航标签其实就是一种文字表达形式,我们用标签来代表网站上的各种分类信息。比如“联系我们”这个标签,代表的内容通常
- 需要的软件phpStudy 用来导入一个数据库api-server 数据库功能可以开启一个服务器,让开发环境可以使用生产环境的网址请求安装
- 1 运行SQLPLUS工具 sqlplus 2 以OS的默认身份连接 / as sysdba 3 显示当前用户名 show user 4 直
- 今天写了一个放迅雷焦点广告的效果,还请大家多多指正,先附上效果图一张:相关文章:迅雷首页新闻图片轮播效果js源码首先是JS代码部分,之前一定
- SQL中的单记录函数 1.ASCII 返回与指定的字符对应的十进制数; SQL> select ascii('A')
- 1、首先在系统盘中查找scrrun.dll,如果存在这个文件,请跳到第三步,如果没有,请执行第二步。 2、在安装文件目录i386中找到scr
- 内容摘要:当我们不想让某IP服务我们的网站时,我们就要写段程序来限制IP地址。asp中如何对ip进行过滤限制?本文介绍了一种方法,这个函数只
- 一、概述公司新购了一批PC,准备把几个性能较优的PC升级为数据库服务器,替换老旧的机器。公司有套POS终端软件,后台数据存储是 MySQL
- 1:在终端下:mysql -V。 以下是代码片段:[shengting@login ~]$ mysql -Vmysql Ver 14.7 D
- 在后台处理数据时,前台页面同时计数显示进度条Proess Bar使用了layer来显示用法:1。引用<script lang
- Dreamweaver MX 2004 试用试用心得:安装:选择工作界面(我选了默认的设计模式)初次启动,选择30天试用如果你也看到这个警告
- 译者newstart写的简介:Ross Dawson所在的Future Exploration Network于2007年5月发布的web2
- 1.由于设置了slave的配置信息,mysql在数据库data目录下生成master.info,所以如有要修改相关slave的配置要先删除该
- 在MySQL中,如何实现Top N及M至N段的记录查询?我们可以利用MySQL中SELECT支持的一个子句——LIMIT——来完成这项功能。