golang逐行读取文件的操作
我就废话不多说了,大家还是直接看代码吧~
funcReadLine(fileNamestring)([]string,error){
f,err:=os.Open(fileName)
iferr!=nil{
returnnil,err
}
buf:=bufio.NewReader(f)
varresult[]string
for{
line,err:=buf.ReadString('\n')
line=strings.TrimSpace(line)
iferr!=nil{
iferr==io.EOF{//读取结束,会报EOF
returnresult,nil
}
returnnil,err
}
result=append(result,line)
}
returnresult,nil
}
补充:Golang读取文件和处理超大文件方案
Golang操作文件的读取的方法很多,适用的场景也是各不相同,在此我们将文件的读取分为如下几种:
文件整体读取
文件分片读取(块级读取)
文件行级读取
系统的配置不同,执行的耗时也不相同,此处给出一参考
系统配置:
OS:Windows10
Memory:16G
CPU(英特尔)Intel®Core™i3-4370CPU@3.80GHz(3800MHz)
1.文件整体读取
文件整体读取就是将文件一次性读取到,理解上是将文件的内容第一次就读取完了
使用场景:
针对小文件比较合适(大文件读取空间和时间的消耗也很大)
对于整体性强的文件也比较合适(文件也不能太大)
代码示例1
packagemain
import(
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
//测试用的文本文件11M大小
varm11string=`G:\runtime\log\ccapi\11M.log`
//测试用的文本文件400M大小
varm400string=`G:\runtime\log\ccapi\400M.log`
//将整个文件都读取
funcreadAll(filePathstring){
start1:=time.Now()
ioutil.ReadFile(filePath)
fmt.Println("readAllspend:",time.Now().Sub(start1))
}
funcmain(){
readAll(m11)
readAll(m400)
}
$gorunmain.go readAllspend:6.9999ms readAllspend:358.8014ms
代码示例2
packagemain
import(
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
//测试用的文本文件11M大小
varm11string=`G:\runtime\log\ccapi\11M.log`
//测试用的文本文件400M大小
varm400string=`G:\runtime\log\ccapi\400M.log`
//将文件完整读取
funcreadAllBuff(filePathstring){
start1:=time.Now()
//打开文件
FileHandle,err:=os.Open(filePath)
iferr!=nil{
log.Println(err)
return
}
//关闭文件
deferFileHandle.Close()
//获取文件当前信息
fileInfo,err:=FileHandle.Stat()
iferr!=nil{
log.Println(err)
return
}
buffer:=make([]byte,fileInfo.Size())
//读取文件内容,并写入buffer中
n,err:=FileHandle.Read(buffer)
iferr!=nil{
log.Println(err)
}
//打印所有切片中的内容
fmt.Println(string(buffer[:n]))
fmt.Println("readAllBuffspend:",time.Now().Sub(start1))
}
funcmain(){
readAllBuff(m11)
readAllBuff(m400)
}
2.文件分片读取
对文件一部分一部分逐步的读取,直到文件完全读取完
PS:每次读取文件的大小是根据设置的分片大小,所以对于读取文本类型的文件时(例如:日志文件)
不一定是按照你的期望逐行输出,因为不会处理文本尾部的换行符,而是按照分片大小读取内容
使用场景:
读取超大的文件很合适
读二进制类型的文件很合适(比如:音视频文件或者资源类型文件等)
代码示例
packagemain
import(
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
//测试用的文本文件11M大小
varm11string=`G:\runtime\log\ccapi\11M.log`
//测试用的文本文件400M大小
varm400string=`G:\runtime\log\ccapi\400M.log`
//文件一块一块的读取
funcreadBlock(filePathstring){
start1:=time.Now()
FileHandle,err:=os.Open(filePath)
iferr!=nil{
log.Println(err)
return
}
deferFileHandle.Close()
//设置每次读取字节数
buffer:=make([]byte,1024)
for{
n,err:=FileHandle.Read(buffer)
//控制条件,根据实际调整
iferr!=nil&&err!=io.EOF{
log.Println(err)
}
ifn==0{
break
}
//如下代码打印出每次读取的文件块(字节数)
//fmt.Println(string(buffer[:n]))
}
fmt.Println("readBolckspend:",time.Now().Sub(start1))
}
funcmain(){
readBlock(m11)
readBlock(m400)
}
$gorunmain.go readBolckspend:31.9814ms readBolckspend:1.0889488s
3.文件逐行读取
对文件一行一行的读取,直到读到文件末尾
使用场景:
读取超大的文件很合适(例如:超大log文件等)
读取的文件最好是有换行的(如果使用单行文件组成的大文件,需要注意)
对需要分析内容的大文件
统计某些数据出现的次数
查询某些数据是否存在
查找指定行的数据
示例代码1
packagemain
import(
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
//测试用的文本文件11M大小
varm11string=`G:\runtime\log\ccapi\11M.log`
//测试用的文本文件400M大小
varm400string=`G:\runtime\log\ccapi\400M.log`
//读取文件的每一行
funcreadEachLineReader(filePathstring){
start1:=time.Now()
FileHandle,err:=os.Open(filePath)
iferr!=nil{
log.Println(err)
return
}
deferFileHandle.Close()
lineReader:=bufio.NewReader(FileHandle)
for{
//相同使用场景下可以采用的方法
//func(b*Reader)ReadLine()(line[]byte,isPrefixbool,errerror)
//func(b*Reader)ReadBytes(delimbyte)(line[]byte,errerror)
//func(b*Reader)ReadString(delimbyte)(linestring,errerror)
line,_,err:=lineReader.ReadLine()
iferr==io.EOF{
break
}
//如下是某些业务逻辑操作
//如下代码打印每次读取的文件行内容
fmt.Println(string(line))
}
fmt.Println("readEachLineReaderspend:",time.Now().Sub(start1))
}
funcmain(){
readEachLineReader(m11)
readEachLineReader(m400)
}
$gorunmain.go readEachLineReaderspend:16.9902ms readEachLineReaderspend:537.9683ms
代码示例2
packagemain
import(
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"time"
)
//测试用的文本文件11M大小
varm11string=`G:\runtime\log\ccapi\11M.log`
//测试用的文本文件400M大小
varm400string=`G:\runtime\log\ccapi\400M.log`
//读取文件的每一行
funcreadEachLineScanner(filePathstring){
start1:=time.Now()
FileHandle,err:=os.Open(filePath)
iferr!=nil{
log.Println(err)
return
}
deferFileHandle.Close()
lineScanner:=bufio.NewScanner(FileHandle)
forlineScanner.Scan(){
//相同使用场景下可以使用如下方法
//func(s*Scanner)Bytes()[]byte
//func(s*Scanner)Text()string
//实际逻辑:对读取的内容进行某些业务操作
//如下代码打印每次读取的文件行内容
fmt.Println(lineScanner.Text())
}
fmt.Println("readEachLineScannerspend:",time.Now().Sub(start1))
}
funcmain(){
readEachLineScanner(m11)
readEachLineScanner(m400)
}
$gorunmain.go readEachLineScannerspend:17.9895ms readEachLineScannerspend:574.1722ms
4.总结
面试中常见的类似超大文件读取的问题,通常我们采用分片读取或者逐行读取的方案即可
大文件的上传也可以采用类似的解决方案,每次读取文件的部分内容上传(写入)网络接口中,直至文件读取完毕
普通的小文件并且对内容没有太多操作的,可以采用整体读取,速度相对较快
对文件内容有操作的采用分片读取和逐行读取更合适
二进制类型文件采用分片读取或者整体读取的方案比较合适
文件读取不仅是本地文件,要读去网络上的文件(各种文档,音视频,图片,和其他各种类型文件)时要访问到文件获取io.ReadCloser或者io.Reader后可以采用三种方式将文件内容读取到
funcReadAll(rio.Reader)([]byte,error)文件完整读取
funcCopy(dstWriter,srcReader)(writtenint64,errerror)文件读取并写入
typeReaderinterface{
Read(p[]byte)(nint,errerror)
}
通过Reader接口的Read方法读取
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。