Go:“tail -f”式发电机 [英] Go: "tail -f"-like generator
问题描述
def follow(path):
with open(self。路径)作为行:
lines.seek(0,2)#寻找EOF
而真:
line = lines.readline()
如果不是行:
time.sleep(0.1)
继续
收益行
它的作用类似于UNIX tail -f
:你会得到文件最后一行。这很方便,因为你可以在不阻塞的情况下获得生成器并将它传递给另一个函数。
以下是代码:
func关注(fileName字符串) chan string {
$ b $ 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)
去func(){
用于{
line,_,_:= bf.ReadLine()
如果len(line)== 0 {
time.Sleep(10 * time.Millisecond)
} else {
out_chan< - string(line)
}
}
推迟文件。 Close()
close(out_chan)
}()
return out_chan
}
类型tailReader结构{
io.ReadCloser
}
func(t tailReader)Read(b [] byte)(int,error){
for {
n,err:= t.ReadCloser.Read(b)
如果n> 0
return n,nil
} else if err!= io.EOF {
return n,err
}
time.Sleep(10 * time.Millisecond)
$ b $ 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 循环播放行:
t,err:= newTailReader(somefile)
if err!= nil {
log.Fatal(err)
}
推迟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值:
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(value is,v)
}
这个方法对问题中概述的goroutine方法有几个优点。首先是关机很简单。只需关闭文件。没有必要指示它应该退出的goroutine。第二个优点是许多软件包可以与io.Reader一起使用。
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
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.
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.
Here is the code:
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
}
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.
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
}
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)
}
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)
}
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.
这篇关于Go:“tail -f”式发电机的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!