Python f.seek 导致 OSError [Errno 22] 如果手动编辑文本文件但如果程序输出到文件则没有错误

Python f.seek caused OSError [Errno 22] if manually edit the text file but no error if program output to file

我正在尝试从文本文件中获取最后一行,我使用的解决方案是 What is the most efficient way to get first and last line of a text file?

def read_last_line(filename):
    with open(filename, "rb") as f:
        first = f.readline()
        if f.read(1) == '':
            return first
        f.seek(-2, 2)  # Jump to the second last byte.
        while f.read(1) != b"\n":  # Until EOL is found...
            f.seek(-2, 1)  # ...jump back the read byte plus one more.
        last = f.readline()  # Read last line.
        return last.decode('ascii')

如果文件被另一个 script/program 修改,它设法成功获取文本文件的最后一行,但是当我使用 Notepad++ 修改文本文件时,与另一个 Notepad++ 的修改完全相同 script/program,会抛出如下异常:

in read_last_line
    f.seek(-2, 2)
OSError: [Errno 22] Invalid argument

我想做的是,我使用 watchdog 来检查是否有文件更改,并且在修改时我会在修改后的文件上调用 read_last_line

示例文件

11/26/2020 2:05:12 PM Time Updated: +2ms            Regular Update
11/26/2020 2:06:13 PM Time Updated: +4ms            Regular Update
11/26/2020 2:07:13 PM Time Updated: +1ms            Regular Update

我是如何调用函数的:

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import ntpath

class FileEventHandler(FileSystemEventHandler):
    def __init__(self, filetowatch):
        self.file = filetowatch
    
    def on_modified(self, event):
        modified_file = ntpath.basename(event.src_path)
        if modified_file == self.file:
            read_last_line(event.src_path)

if __name__ == "__main__":
    event_handler = FileEventHandler("sample.txt")
    observer = Observer()
    observer.schedule(event_handler, path='C:/Program Files (x86)/SomeTime', recursive=False)
    observer.start()

我可以知道是否有人知道导致错误的原因吗?

平台:Windows10,Python3.7.4

更新 - 答案

所以这个错误是因为 fread(1) == '' 使用 falsetru 的解决方案修复的。

它没有按我预期的方式执行的原因是因为文本编辑器 删除了 示例文件并使用相同的文件名创建了一个文件,因此 fread(1)==''被触发(抛出)并且使用script/program修改示例文件并没有抛出只是因为我没有删除文件。

如果只有一个(带/不带尾随换行符),则永远不会满足 while 循环条件。

这导致 f.seek(-2, 1) 尝试查找导致错误的负文件位置。

保护这种情况(防止试图超出文件开头),使用 .tell() 让您知道当前文件位置:

        while f.tell() >= 1 and f.read(1) != b"\n":

或者您可以使用 seek(..) return 值:

        while f.read(1) != b"\n":
            if f.seek(-2, 1) == 0:
                break  # prevent going beyond file beginning.

更新

在二进制模式下 <io object>.read() return 字节对象。在 if 条件下,代码将字节对象与字符串 '' 进行比较;由于类型差异,它总是会失败。 更改为与字节文字进行比较将解决此问题。

        if f.read(1) == b'':
            return first