怎么在Golang中读取超大文件
怎么在Golang中读取超大文件?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
什么是golang
golang 是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言,其语法与 C语言相近,但并不包括如枚举、异常处理、继承、泛型、断言、虚函数等功能。
Golang超大文件读取的两个方案
流处理方式
分片处理
去年的面试中我被问到超大文件你怎么处理,这个问题确实当时没多想,回来之后仔细研究和讨论了下这个问题,对大文件读取做了一个分析
比如我们有一个log文件,运行了几年,有100G之大。按照我们之前的操作可能代码会这样写:
func ReadFile(filePath string) []byte{ content, err := ioutil.ReadFile(filePath) if err != nil { log.Println("Read error") } return content}
上面的代码读取几兆的文件可以,但是如果大于你本身及其内存,那就直接翻车了。因为上面的代码,是把文件所有的内容全部都读取到内存之后返回,几兆的文件,你内存够大可以处理,但是一旦上几百兆的文件,就没那么好处理了。
那么,正确的方法有两种
第一个是使用流处理方式代码如下
func ReadFile(filePath string, handle func(string)) error { f, err := os.Open(filePath) defer f.Close() if err != nil { return err } buf := bufio.NewReader(f) for { line, err := buf.ReadLine("\n") line = strings.TrimSpace(line) handle(line) if err != nil { if err == io.EOF{ return nil } return err } return nil }}
第二个方案就是分片处理
当读取的是二进制文件,没有换行符的时候,使用下面的方案一样处理大文件
func ReadBigFile(fileName string, handle func([]byte)) error { f, err := os.Open(fileName) if err != nil { fmt.Println("can't opened this file") return err } defer f.Close() s := make([]byte, 4096) for { switch nr, err := f.Read(s[:]); true { case nr < 0: fmt.Fprintf(os.Stderr, "cat: error reading: %s\n
补充:golang 读取大文件处理sync.pool + bufio.NewReader(f)
看代码吧~
文件大小
package mainimport ("bufio""fmt""io"//"math""os""strings""sync""time")func main() {var (s time.Time //当前时间file *os.FilefileStat os.FileInfoerr errorlastLineSize int64)s = time.Now()if file, err = os.Open("/Users/zhangsan/Downloads/log.txt");err != nil{fmt.Println(err)}defer func() {err = file.Close() //close after checking err}()//queryStartTime, err := time.Parse("2006-01-02T15:04:05.0000Z", startTimeArg)//if err != nil {//fmt.Println("Could not able to parse the start time", startTimeArg)//return//}////queryFinishTime, err := time.Parse("2006-01-02T15:04:05.0000Z", finishTimeArg)//if err != nil {//fmt.Println("Could not able to parse the finish time", finishTimeArg)//return//}if fileStat, err = file.Stat();err != nil {return}fileSize := fileStat.Size()//72849354767offset := fileSize - 1//检测是不是都是空行 只有\nfor {var (b []byten intchar string)b = make([]byte, 1)//从指定位置读取if n, err = file.ReadAt(b, offset);err != nil {fmt.Println("Error reading file ", err)break}char = string(b[0])if char == "\n" {break}offset--//获取一行的大小lastLineSize += int64(n)}var (lastLine []bytelogSlice []stringlogSlice1 []string)//初始化一行大小的空间lastLine = make([]byte, lastLineSize)_, err = file.ReadAt(lastLine, offset)if err != nil {fmt.Println("Could not able to read last line with offset", offset, "and lastline size", lastLineSize)return}//根据条件进行区分logSlice = strings.Split(strings.Trim(string(lastLine),"\n"),"next_pay_date")logSlice1 = strings.Split(logSlice[1],"\"")if logSlice1[2] == "2021-06-15"{Process(file)}fmt.Println("\nTime taken - ", time.Since(s))fmt.Println(err)}func Process(f *os.File) error {//读取数据的key,减小gc压力linesPool := sync.Pool{New: func() interface{} {lines := make([]byte, 250*1024)return lines}}//读取回来的数据池stringPool := sync.Pool{New: func() interface{} {lines := ""return lines}}//一个文件对象本身是实现了io.Reader的 使用bufio.NewReader去初始化一个Reader对象,存在buffer中的,读取一次就会被清空r := bufio.NewReader(f) ////设置读取缓冲池大小 默认16r = bufio.NewReaderSize(r,250 *1024)var wg sync.WaitGroupfor {buf := linesPool.Get().([]byte)//读取Reader对象中的内容到[]byte类型的buf中n, err := r.Read(buf)buf = buf[:n]if n == 0 {if err != nil {fmt.Println(err)break}if err == io.EOF {break}return err}//补齐剩下没满足的剩余nextUntillNewline, err := r.ReadBytes('\n')//fmt.Println(string(nextUntillNewline))if err != io.EOF {buf = append(buf, nextUntillNewline...)}wg.Add(1)go func() {ProcessChunk(buf, &linesPool, &stringPool)wg.Done()}()}wg.Wait()return nil}func ProcessChunk(chunk []byte, linesPool *sync.Pool,stringPool *sync.Pool) {//做相应的处理}
执行
go run test2.go "2020-01-01T00:00:00.0000Z" "2020-02-02T00:00:00.0000Z" /Users/zhangsan/go/class="lazy" data-src/workspace/test/log.log
EOFTime taken - 20.023517675s<nil>
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注编程网行业资讯频道,感谢您对编程网的支持。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341