如何使用Go每10秒从一个大文件中读取最后一行

Gok*_*oku 12 go

如何从大日志文件中读取最后两行而不将其完全加载到内存中?

我需要每10秒阅读一次(在Win机器上)...而且我试图读取最后一行...

package main

import (
    "fmt"
    "time"
    "os"
)

const MYFILE = "logfile.log"

func main() {
    c := time.Tick(10 * time.Second)
    for now := range c {
        readFile(MYFILE)
    }
}

func readFile(fname string){
    file, err:=os.Open(fname)
    if err!=nil{
        panic(err)
    }
    buf:=make([]byte, 32)
    c, err:=file.ReadAt(32, ????)
    fmt.Printf("%s\n", c)


}
Run Code Online (Sandbox Code Playgroud)

日志文件类似于:

07/25/2013 11:55:42.400, 0.559
07/25/2013 11:55:52.200, 0.477
07/25/2013 11:56:02.000, 0.463
07/25/2013 11:56:11.800, 0.454
07/25/2013 11:56:21.600, 0.424
07/25/2013 11:56:31.400, 0.382
07/25/2013 11:56:41.200, 0.353
07/25/2013 11:56:51.000, 0.384
07/25/2013 11:57:00.800, 0.393
07/25/2013 11:57:10.600, 0.456
Run Code Online (Sandbox Code Playgroud)

谢谢!

Jos*_*hua 11

您可以使用file.Seek()file.ReadAt()几乎结束然后再向前阅读.除非你知道2行= x字节,否则你只能估计从哪里开始寻找.

您可以使用os.Stat(名称)获取文件长度

以下是基于ReadAt,Stat和示例日志文件的示例:

package main

import (
    "fmt"
    "os"
    "time"
)

const MYFILE = "logfile.log"

func main() {
    c := time.Tick(10 * time.Second)
    for _ = range c {
        readFile(MYFILE)
    }
}

func readFile(fname string) {
    file, err := os.Open(fname)
    if err != nil {
        panic(err)
    }
    defer file.Close()

    buf := make([]byte, 62)
    stat, err := os.Stat(fname)
    start := stat.Size() - 62
    _, err = file.ReadAt(buf, start)
    if err == nil {
        fmt.Printf("%s\n", buf)
    }

}
Run Code Online (Sandbox Code Playgroud)

  • @Joshua - 读取我们不知道字节大小的行怎么样? (5认同)

Bac*_*sme 10

有些人会来到这个页面寻找有效地读取日志文件的最后一行(如 tail 命令行工具)。

这是我读取大文件最后一行的版本。它使用之前的两个建议(使用Seek和文件Stat)。

它逐字节向后读取文件(无需设置缓冲区大小),直到找到行的开头或文件的开头。

func getLastLineWithSeek(filepath string) string {
    fileHandle, err := os.Open(filepath)

    if err != nil {
        panic("Cannot open file")
        os.Exit(1)
    }
    defer fileHandle.Close()

    line := ""
    var cursor int64 = 0
    stat, _ := fileHandle.Stat()
    filesize := stat.Size()
    for { 
        cursor -= 1
        fileHandle.Seek(cursor, io.SeekEnd)

        char := make([]byte, 1)
        fileHandle.Read(char)

        if cursor != -1 && (char[0] == 10 || char[0] == 13) { // stop if we find a line
            break
        }

        line = fmt.Sprintf("%s%s", string(char), line) // there is more efficient way

        if cursor == -filesize { // stop if we are at the begining
            break
        }
    }

    return line
}
Run Code Online (Sandbox Code Playgroud)