这个小程序要实现的效果,简单地说,就是将目标文件的内容读取输出到终端,并且目标文件并不是静态的,而是随时会添加新的内容。我们的目标就是一旦目标文件添加了新的内容,就把它读取出来并且显示到终端上。
实现方法很简单,用一个变量offset标记已经读到了文件的哪个位置,每次循环开始前就将读指针指到相应位置。这里有两个tricky的地方需要注意,我们在每次循环的时候都要重复地打开和关闭文件,否则文件有更新也读不出来。当我们读到文件末尾时,会多读出一个字符(文件结束标识符?)。具体的实现如下:
------注:文件末尾的最后一个字符并不是文件描述符,其实就是一个简单的' ',一般用"echo"命令或者"vim"编写文件时,就会在末尾自动添加。
方法一:独立实现
package main import ( "fmt" "io" "os" "time" ) func main() { file := os.Args[1] buf := make([]byte, 1024) offset := 0 f, err := os.Open(file) defer f.Close() if err != nil { fmt.Printf("open file %s failed: %v ", file, err) return } for { n, err := f.Read(buf) if err != nil && err != io.EOF { fmt.Printf("read file %s failed: %v ", file, err) return } if n > 1 { if n != len(buf) { n-- } fmt.Printf("%s", string(buf[:n])) offset += n } if n != len(buf) { time.Sleep(1 * time.Second) f.Close() f, err = os.Open(file) if err != nil { fmt.Printf("open file %s failed: %v ", file, err) return } f.Seek(int64(offset), 0) } } }
方法二:实现io.Reader接口,装饰bufio库
package main import ( "os" "bufio" "time" "io" "fmt" ) type fileReader struct { file string offset int64 } func (f *fileReader) Read(p []byte) (n int, err error) { reader, err := os.Open(f.file) defer reader.Close() if err != nil { return 0, err } reader.Seek(f.offset, 0) n, err = reader.Read(p) if err == io.EOF { time.Sleep(1 * time.Second) } f.offset += int64(n) return n, err } func main() { file := &fileReader{os.Args[1], 0} br := bufio.NewReader(file) for { log, _, err := br.ReadLine() if err == io.EOF { continue } if err != nil { fmt.Printf("err: %v", err) return } fmt.Printf("%s ", string(log)) } }