2015-06-29 45 views
10

我在Python有這個方便的功能:轉到:「尾-f」般的發電機

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東西:你得到一個文件的最後幾行,因爲他們來。這很方便,因爲您可以在不阻止的情況下獲取生成器並將其傳遞給另一個函數。

然後我不得不在Go中做同樣的事情。我是這種語言的新手,所以我不確定我所做的是否符合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做到這一點?我有一種感覺,使用異步調用這樣的事情是一個矯枉過正的事情,它真的困擾我。

+2

使用'os.SEEK_END'而不是'2'。睡覺(10)'睡10納秒,你可能意思是'睡覺(10 *時間。毫秒)'。一旦'io.Reader'給你'io.EOF',你不應該指望它給你更多的數據;文件/蒸汽的EOF == **結束**,不再有數據。 –

+0

謝謝你,我編輯了代碼。 – oopcode

回答

7

我建議創建圍繞在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

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值用於循環:

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。第二個優點是許多軟件包可以與io.Reader一起使用。

+0

有兩件事,我看到的是一個錯誤.... [1] time.Sleep(10 * time.Millisecond)將導致系統產生熱量,而不是靜靜地坐着,直到實際上有一些數據。 [2]當你在文件末尾時,主循環沒有信號,所以你可以決定退出。 (我有一個用例,我向服務發送了一些命令,然後監視日誌等待正面回覆;由於沒有負面回覆,我需要耗盡數據或檢查計時器,Goroutines和關閉數據源是不是一種選擇。 – Richard