我制作的脚本会在文件的特定位置显示文字。但是,seek()和read()的计数方式存在差异。它是这样的。
我的文字文件是:
1
%
2
%
―
%
4
%
5
%
6
' - '在第5行是一个水平条(unicode 0x2015)而不是破折号。 '%'作为分隔符。
以下数据用作文件索引
0 2 ['1\n']
4 2 ['2\n']
8 4 ['―\n']
14 2 ['4\n']
18 2 ['5\n']
22 2 ['6\n']
第一列是文件中字符串的位置(数字),第二列是长度,3td是要显示的文本(第1,3,5,7,9,11行中的数字)文本文件)。
我试图在特定位置读取文件,如下所示:
f = open('myfile.txt', 'r', encoding='utf-8')
f.seek(start)
text = f.read(length)
f.close()
其中'开始'和'长度'是索引文件的第一列和第二列,' text'是要显示的文本。这非常适合显示索引文件中除第5个(带有水平条的那个)之外的所有行的内容,因为seek()将水平条的长度解释为3,因此索引中的总长度为4文件(3为水平条,1为' \ n'),而read()将水平线的长度解释为只有一个,从而创建以下输出:
―
%
(blank space)
也就是说,它包括水平条,它的' \ n',分隔符及其' \ n' (四个字)。这种效果是累积的,更多的水平条或任何其他不在utf-8中的unicode字符会增加错误显示的行数。
有关如何解决这个问题的想法吗?
答案 0 :(得分:4)
seek
始终以字节为单位, * 不是字符,甚至是for files opened in text mode。
否则它无法有效地远程工作 - 否则UTF-8文本文件中的第100个字符可能是字节1,000,000或字节2,739,184,唯一的方法是回到启动并编码999,999个字符。 **
但如果您处于二进制模式,read
只读取字节数;在文本模式下,这些字节在运行中被解码为Unicode字符串。 (由于您按顺序读取文件,因此通常不会出现性能问题 - 但是当它出现时,您总是会得到二进制模式。)
如果你有一个已知的职位,你希望能够返回,你可以"标记"它通过以后再调用tell
然后调用seek
,但除此之外,在文本文件中寻找并不是非常有用,当然除了文件的开头或结尾。
*事实上,它甚至没有记录为文本文件的字节;除0或&#34之外的任何东西;不透明的数字"由tell
返回产生"未定义的行为"。我相信它总会寻找确切的指定字节位置 - 但由于解码器管道的工作方式,即使你没有寻找到字符的中间,这也会导致mojibake,特别是对于使用移位码的编码。为了处理这些情况,tell
创建了可以在以后seek
恢复的特殊快照,但当然没有文件中某些随机点的快照。
**这并不完全正确 - 您可以在阅读时或在您尝试寻找的时候建立一个抵消表,甚至可以提前阅读。但这绝对不是你希望Python在每个文件上做的事情,只是因为你想通过字符索引寻找的极少数情况;它是你想要专门调整你关心的罕见案例的东西。 linecache
模块 - 因为调试器需要它而在标准库中 - 完成大致相同的工作,并且只要忽略有关tokenizer的位,就会附带pretty readable source,所以如果你想构建一个字符索引器自己,它可能是很好的示例代码来开始。
答案 1 :(得分:3)
在python 3中,当您以文本模式打开文件时,例如" r",您和原始文件之间有一个解码器。在这种情况下,它的UTF-8解码器。 "文件位置"并不真正有意义,因为文本级别的字符索引与文件中的字节索引不同。另外,python会缓存后台以帮助解码。
解决方案是读取二进制文件并稍后进行解码
f = open('myfile.txt', 'rb')
f.seek(start)
text = f.read(length).decode(encoding='utf-8')
f.close()