类似于"tail -f"的生成器 [英] "tail -f"-like generator

查看:96
本文介绍了类似于"tail -f"的生成器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Python中有这个方便的功能:

I had this convenient function in Python:

def follow(path):
    with open(self.path) as lines:
        lines.seek(0, 2)  # seek to EOF

        while True:
            line = lines.readline()
            if not line:
                time.sleep(0.1)
                    continue
                yield line 

它执行与UNIX tail -f类似的操作:在文件的最后几行出现时,您会得到它们.之所以方便,是因为您可以获取生成器而不会阻塞,并将其传递给另一个函数.

It does something similar to UNIX tail -f: you get last lines of a file as they come. It's convenient because you can get the generator without blocking and pass it to another function.

然后我必须在Go中执行相同的操作.我是这门语言的新手,所以我不确定我所做的操作是否足够习惯/正确.

Then I had to do the same thing in Go. I'm new to this language, so I'm not sure whether what I did is idiomatic/correct enough for Go.

这是代码:

func Follow(fileName string) chan string {

    out_chan := make(chan string)

    file, err := os.Open(fileName)
    if err != nil {
        log.Fatal(err)
    }

    file.Seek(0, os.SEEK_END)
    bf := bufio.NewReader(file)

    go func() {
        for {
            line, _, _ := bf.ReadLine()

            if len(line) == 0 {
                time.Sleep(10 * time.Millisecond)
            } else {
                out_chan <- string(line)
            }
        }

        defer file.Close()
        close(out_chan)
    }()

    return out_chan
}

在Go中,有没有更清洁的方法来做到这一点?我觉得对这样的事情使用异步调用实在是太过分了,这真的使我感到困扰.

Is there any cleaner way to do this in Go? I have a feeling that using an asynchronous call for such a thing is an overkill, and it really bothers me.

推荐答案

我建议为在EOF上休眠的阅读器创建包装器:

I suggest creating a wrapper around a reader that sleeps on EOF:

type tailReader struct {
    io.ReadCloser
}

func (t tailReader) Read(b []byte) (int, error) {
    for {
        n, err := t.ReadCloser.Read(b)
        if n > 0 {
            return n, nil
        } else if err != io.EOF {
            return n, err
        }
        time.Sleep(10 * time.Millisecond)
    }
}

func newTailReader(fileName string) (tailReader, error) {
    f, err := os.Open(fileName)
    if err != nil {
        return tailReader{}, err
    }

    if _, err := f.Seek(0, 2); err != nil {
        return tailReader{}, err
    }
    return tailReader{f}, nil
}

此阅读器可以在io.Reader可以使用的任何地方使用.使用 bufio.Scanner

This reader can be used anywhere an io.Reader can be used. Here's how loop over lines using bufio.Scanner:

t, err := newTailReader("somefile")
if err != nil {
    log.Fatal(err)
}
defer t.Close()
scanner := bufio.NewScanner(t)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
    fmt.Fprintln(os.Stderr, "reading:", err)
}

阅读器还可用于循环附加到文件的JSON值:

The reader can also be used to loop over JSON values appended to the file:

t, err := newTailReader("somefile")
if err != nil {
    log.Fatal(err)
}
defer t.Close()
dec := json.NewDecoder(t)
for {
    var v SomeType
    if err := dec.Decode(&v); err != nil {
       log.Fatal(err)
    }
    fmt.Println("the value is ", v)
}

与问题中概述的goroutine方法相比,该方法具有许多优点.首先是关机很容易.只需关闭文件.无需向goroutine发出退出信号的信号.第二个优点是许多软件包都可以与io.Reader一起使用.

There are a couple of advantages this approach has over the goroutine approach outlined in the question. The first is that shutdown is easy. Just close the file. There's no need to signal the goroutine that it should exit. The second advantage is that many packages work with io.Reader.

可以调整睡眠时间,以满足特定需求.减少时间以降低延迟,并增加时间以减少CPU使用量.睡眠100毫秒对于显示给人类的数据可能足够快.

The sleep time can be adjusted up or down to meet specific needs. Decrease the time for lower latency and increase the time to reduce CPU use. A sleep of 100ms is probably fast enough for data that's displayed to humans.

这篇关于类似于"tail -f"的生成器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆