實現相似tail -f file功能

python版本py3python

tail -f file是打印最後10行,而後跟蹤文件追加的內容打印出來。this

python3 覺得本方式打開的話,不能回退(f.seek(-1,1)),全部以'rb'方式打開文件。編碼

思路是f.seek(-n,2)從文件末尾回退n字節,而後f.readlines()讀取文件,若是讀到的少於10行,則再往前移動n字節,直到讀到11行,而後打印出來,再跟蹤打印文件追加的內容,並打印。spa

知識點:指針

f.tell()返回當前的文件位置code

f.seek(n,m),m=0從文件開頭往前或日後移到n字節,負數表示像文件頭方向移動,正數表示向文件尾移動,m=1表示從文件指針當前位置移動,m=2表示從文件末尾開始移動,指針超出邊界會報錯。blog

f.seek(0,2)移到文件末尾內存

open(file,'r')以文本方式打開,open(file,'rb')以二進制方式打開。utf-8

因爲文檔是utf8或gbk編碼保存的文件,以二進制方式打開文件讀取到的是uft8或gbk編碼(字節),全部打印是須要解碼。unicode

 

我的以爲在py2中,

"我」是str類型,utf8或gbk編碼保存在內存中,只能進行解碼成unicode操做:

 

py3中

‘我’是str類型,以unicode編碼放在內存中,只能解碼成bytes操做:

 

具體代碼實現:

 1 #! -*- coding:utf-8 -*-
 2 # runing on python3
 3 import os,sys
 4 from time import sleep
 5 COUDING=sys.getfilesystemencoding()
 6 
 7 class tail(object):
 8 
 9     def __init__(self,n,filename,callback):
10         self._n = n
11         self.filename= filename
12         self.callback = callback
13         self.track()
14 
15     def track(self):
16         if os.path.exists(self.filename):
17             if os.path.isfile(self.filename):
18                 try:
19                     with open(self.filename,'rb') as f:
20                         self._file = f
21                         # 文件內部指針移到末尾
22                         f.seek(0, os.SEEK_END)
23                         self._len = f.tell()
24                         self.showline()
25                         while True:
26                             line = f.readline()
27                             self.callback(line)
28                             sleep(0.5)
29                 except Exception as e:
30                     print(e)
31             else:
32                 sys.stdout.write("tail: %s: cannot follow end of this type of file; giving up on this name"%self.filename)
33         else:
34             sys.stdout.write("tail: cannot open `%s' for reading: No such file or directory"%self.filename)
35 
36     def showline(self):
37         # 假設每行100個字節
38         line_len = 100
39         n = self._n
40         # 文件字節數小於500,直接從頭讀文件,而後取後5行
41         if self._len < n * line_len:
42             self._file.seek(0)
43             lines = self._file.readlines()[-self._n:]
44             self.callback(lines)
45         else:
46             # 指針總文件末尾往文件頭移動n * line-len個字節
47             while True:
48                 self._file.seek(-n*line_len,os.SEEK_END)
49                 lines = self._file.readlines()
50                 # 讀n+1行,由於seek是以字節爲單位移到的,若是移到一箇中文字符的編碼的中間,會出現UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
51                 if len(lines) > self._n:
52                     self.callback(lines[-self._n:])
53                     return
54                 else:
55                     n += 1
56 
57 def printline(lines):
58     if isinstance(lines,list):
59         for line in lines:
60             sys.stdout.write(line.decode(COUDING))
61     elif isinstance(lines,bytes):
62         sys.stdout.write(lines.decode(COUDING))
63 
64 if __name__ == "__main__":
65     if len(sys.argv) < 2:
66         print("xxx")
67     else:
68         # 可加參數解析模塊來獲取文件名和顯示最後多少行
69         tail(10,sys.argv[1],printline)
相關文章
相關標籤/搜索