Golang 超大文件读取的两个方案

Golang超大文件读取的两个方案

  1. 流处理方式
  2. 分片处理

去年的面试中我被问到超大文件你怎么处理,这个问题确实当时没多想,回来之后仔细研究和讨论了下这个问题,对大文件读取做了一个分析

比如我们有一个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", err.Error())
            os.Exit(1)
        case nr == 0: // EOF
            return nil
        case nr > 0:
            handle(s[0:nr])
        }
    }
    return nil
}

希望能对学习go语言的同学有所帮助。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 1

你好,我还是上次提问的那个,因为针对你的答复,再次提问的时候,贴的代码有问题,删除,准备重新贴的时候,不小心把提问删除了,这里重新描述下我请教的问题:

func main() {
    //文件utf8编码,这里举例下文件内容,
    //fileContent := `内容有20个字节`
    s := make([]byte, 5)
    fp, err := os.Open("./t.txt")
    if err != nil {
        panic(err)
    }

    for {
        n, err := fp.Read(s[:])
        if err != nil {
            if err == io.EOF { //结尾
                break
            }

            fmt.Printf("read line err:%v", err)
        } else if n <= 0 { //结尾
            break
        } else {
            fmt.Println(string(s[:n]))
            //第一次读取内容是:内� ;这是因为读取了5个字节,其中"容"字只读取了2个字节,所以乱码了
            //第二次读取内容是:�有2 ;这里也是同理,读取了"容"字的第三个字节,所以乱码
            // ...
            //因此这里需要判断每次读取的内容尾部的1-2个字节,要判断是否汉字的一部分,进而拿出来,和下一次读取内容的头部的1~2个字节做拼接
            //这样才能保证每次读取的内容是一个完整的可视数据,这里想请教大佬的是这个操作怎么做

        }
    }
}
1个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!