【问题标题】:Python f.seek caused OSError [Errno 22] if manually edit the text file but no error if program output to file如果手动编辑文本文件,Python f.seek 会导致 OSError [Errno 22],但如果程序输出到文件则不会出错
【发布时间】:2021-03-08 23:42:31
【问题描述】:

我正在尝试从文本文件中获取最后一行,我使用了来自 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')

如果文件被另一个脚本/程序修改,它会成功获取文本文件的最后一行,但是当我使用 Notepad++ 修改文本文件时,与另一个脚本/程序的修改完全相同,它会抛出以下例外:

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()

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

平台:Windows 10、Python 3.7.4

更新 - 答案

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

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

【问题讨论】:

  • 当您保存并创建另一个具有相同名称的文件时,Notepad++ 是否可能正在删除该文件?
  • @MarkRansom 它也使用 sublime 和记事本崩溃了。我将添加代码以检查是否触发了文件删除。
  • @MarkRansom 确实,文本编辑器正在删除我的文件,这导致它在我执行 f.read(1) == '' 时抛出错误 - 不使用二进制字符串导致错误。

标签: python python-3.x file-io


【解决方案1】:

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

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

使用.tell() 来保护这种情况(防止试图在文件开头之外寻找),让您知道当前文件位置:

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

或者你可以使用seek(..)返回值:

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

更新

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

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

【讨论】:

  • 这并不能解决问题..它没有抛出错误,因为文件位置错误。我所做的完全相同的修改并使用脚本输出到文件不会导致错误,并将返回正确的最后一行。
  • 感谢您的视频,但我已经尝试了您提供的两种解决方案,但仍然是同一个问题。如果通过脚本/程序/命令提示符修改没有错误(我使用的是Windows)但通过Notepad++/sublime/notepad修改时出错..
  • @Potatoismyname,您能否提供您使用的文件。以及调用函数read_last_line的代码行?
  • 我已经用一个示例文件更新了这个问题。我只是使用文本编辑器将同一行复制并粘贴到文件底部。
猜你喜欢
  • 1970-01-01
  • 2013-10-21
  • 1970-01-01
  • 2020-01-19
  • 1970-01-01
  • 2020-10-14
  • 1970-01-01
  • 1970-01-01
  • 2015-05-27
相关资源
最近更新 更多