python seek()和read()对文件位置的计数不同

2024-04-19 03:25:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在制作一个脚本,在文件的特定位置显示文本。但是seek()和read()的计数方式存在差异。事情是这样的。

我的文本文件是:

1
%
2
%
―
%
4
%
5
%
6

第五行中的“-”是一个水平条(unicode 0x2015)而不是短划线。“%”用作分隔符。

以下数据用作文件索引

^{pr2}$

第一列是字符串在文件中的位置(数字),第二列是长度,第三列是要显示的文本(文本文件第1、3、5、7、9、11行中的数字)。

我试图在以下特定位置读取文件:

f = open('myfile.txt', 'r', encoding='utf-8')
f.seek(start)
text = f.read(length)
f.close()

其中“start”和“length”是索引文件的第一列和第二列,“text”是要显示的文本。这对于显示索引文件中除第5行(带有横条的行)以外的所有行的内容非常有用,因为seek()将水平条的长度解释为3,因此索引文件中的总长度为4(3表示水平条,1 por表示'\n'),而read()解释水平线的长度作为唯一一个这样创建以下输出:

―
%
(blank space)

也就是说,它包括水平条、它的'\n'、分隔符和它的'\n'(四个字符)。这种影响是累积的,水平条或任何其他不在utf-8中的unicode字符越多,错误显示的行数就会增加。

有什么办法解决这个问题吗?


Tags: 文件text文本脚本readunicode水平seek
2条回答

在python3中,当您以文本模式打开文件时,例如“r”,在您和原始文件之间有一个解码器。在本例中,它是UTF-8解码器。”“文件位置”实际上没有意义,因为文本级别的字符索引与文件中的字节索引不同。此外,python缓存后台以帮助解码。在

解决方案是读入二进制,然后再进行解码

f = open('myfile.txt', 'rb')
f.seek(start)
text = f.read(length).decode(encoding='utf-8')
f.close()

^{}总是以字节为单位,*不是字符,甚至是for files opened in text mode。在

它不可能远程高效地工作,否则UTF-8文本文件中的第一百万个字符可能位于字节1000000或字节2739184,唯一的方法是返回起始处并对999999个字符进行编码。**

但是{}只在二进制模式下读取字节;在文本模式下,这些字节会被动态解码为Unicode字符串。(由于您是按顺序读取文件的,这通常不是性能问题,但如果是这样,您总是使用二进制模式。)

如果您想返回一个已知的位置,您可以通过调用tell并稍后再调用seek来“标记”它,但是在其他情况下,查找在文本文件中并不是很有用,当然除了文件的开头或结尾。在


*事实上,对于文本文件,它甚至没有被记录为字节;除了0或tell返回的“不透明数字”以外的任何内容都会产生“未定义的行为”。我相信它总是会搜索到确切指定的字节位置,但由于解码器管道的工作方式,即使不搜索字符的中间,这也会导致mojibake,尤其是使用移位码的编码。为了处理这些情况,tell会生成特殊的快照,这些快照可以在以后的seek上恢复,但当然文件中的某些随机点没有快照。

**这并不完全正确,你可以在阅读时,或者在你试图寻找的时候,甚至可以通过提前阅读来建立一个偏移表。但是,这绝对不是您希望Python在每个文件上都做的事情,只针对您希望按字符索引查找的少数情况;而是您希望针对您关心的不常见情况进行专门调整。标准库中的^{}模块,因为调试器需要它做大致相同的工作,只要忽略标记器的位,它就带有pretty readable source模块,因此,如果您想自己构建字符索引器,开始可能是一个好的示例代码。

相关问题 更多 >