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)