从文件末尾查找并引发不支持的异常
- 2025-03-20 08:47:00
- admin 原创
- 37
问题描述:
我有这段代码片段,并尝试使用 python 从文件末尾向后查找:
f=open('D:SGStat.txt','a');
f.seek(0,2)
f.seek(-3,2)
运行时会引发以下异常:
f.seek(-3,2)
io.UnsupportedOperation: can't do nonzero end-relative seeks
我这里遗漏了什么吗?
解决方案 1:
来自Python 3.2 及更高版本的文档:
在文本文件(模式字符串中没有 的打开文件
b
)中,只允许相对于文件开头进行搜索(例外情况是搜索到以 结尾的文件seek(0, 2)
)。
这是因为文本文件在编码字节和它们所代表的字符之间没有一一对应的关系,因此seek
无法知道在文件中跳转到哪里移动一定数量的字符。
如果您的程序可以使用原始字节来工作,那么您可以将程序更改为:
f = open('D:SGStat.txt', 'ab')
f.seek(-3, 2)
请注意b
模式字符串中的二进制文件。(另请注意删除了冗余f.seek(0, 2)
调用。)
但是,您应该意识到,b
在读取或写入文本时添加标志可能会产生意想不到的后果(例如多字节编码),并且实际上会改变读取或写入的数据类型。
解决方案 2:
现有的答案确实回答了问题,但没有提供解决方案。
正如评论中指出的那样,这个答案是基于未定义的行为,并且不处理 UnicodeDecodeError,您可能会在 UTF-8 文件中遇到此错误。只要您搜索到字符的开头,它就可以很好地处理 ASCII 和其他固定宽度编码。请参阅Philip 的答案,其中包括一种解决方法和进一步的评论,讨论了为什么在 UTF-8 中向后搜索是一个问题。
来自readthedocs:
如果文件以文本模式打开(不带
b
),则只有返回的偏移量tell()
才是合法的。使用其他偏移量会导致未定义的行为。
文档支持这一点,其中指出:
在文本文件(在模式字符串中没有打开的文件)中,只允许
b
相对于文件开头[os.SEEK_SET
]进行搜索......
这意味着如果你有来自旧 Python 的这段代码:
f.seek(-1, 1) # seek -1 from current position
在 Python 3 中它看起来像这样:
f.seek(f.tell() - 1, os.SEEK_SET) # os.SEEK_SET == 0
解决方案
把这些信息放在一起,我们可以实现 OP 的目标:
f.seek(0, os.SEEK_END) # seek to end of file; f.seek(0, 2) is legal
f.seek(f.tell() - 3, os.SEEK_SET) # go backwards 3 bytes
解决方案 3:
Eric Lindsey 的答案不起作用,因为 UTF-8 文件的每个字符可以有多个字节。更糟糕的是,对于我们这些以英语为母语并且只处理英语文件的人来说,它可能只在生产代码中工作一段时间,然后才真正破坏事情。
以下答案基于未定义的行为
...但目前它对 UTF-8 有效,至少适用于 Python 3.7 和 Python 3.12
要在文本模式下向后查找文件,只要您正确处理UnicodeDecodeError
因查找非 UTF-8 字符开头的字节而导致的问题,您就可以这样做。由于我们是向后查找,因此我们可以简单地向后查找一个额外的字节,直到找到字符的开头。
对于 UTF-8 文件,结果f.tell()
仍然是文件中的字节位置,至少目前如此。因此,f.seek()
当您随后将 指向无效偏移量时,将引发 UnicodeDecodeError,f.read()
并且可以通过f.seek()
再次指向不同的偏移量来更正此错误。至少目前这种方法有效。
例如,寻找某一行的开头(紧接着 之后`
`):
pos = f.tell() - 1
if pos < 0:
pos = 0
f.seek(pos, os.SEEK_SET)
while pos > 0:
try:
character = f.read(1)
if character == '
':
break
except UnicodeDecodeError:
pass
pos -= 1
f.seek(pos, os.SEEK_SET)
解决方案 4:
为了使用从当前位置开始搜索并结束,您必须以二进制模式打开文本文件。请参阅此示例,其中我创建了一个文件“nums.txt”,并在文件中放入了“ABCDEFGHIJKLMNOPQRSTUVWXYZ”。我从文件中读取字符串“PYTHON”的字母并显示相同的内容。请参阅我在 anaconda 4.2 中的 python 3.6 windows 中运行的代码
>>> file=open('nums.txt','rb')
>>> file.seek(15,0)
15
>>> file.read(1).decode('utf-8')
'P'
>>> file.seek(8,1)
24
>>> file.read(1).decode('utf-8')
'Y'
>>> file.seek(-7,2)
19
>>> file.read(1).decode('utf-8')
'T'
>>> file.seek(7,0)
7
>>> file.read(1).decode('utf-8')
'H'
>>> file.seek(6,1)
14
>>> file.read(1).decode('utf-8')
'O'
>>> file.seek(-2,1)
13
>>> file.read(1).decode('utf-8')
'N'
扫码咨询,免费领取项目管理大礼包!