Python3 (v3.2.2) 写入二进制文件时多余的位
我正在写一个函数,用来把一个二进制文件里的字节映射到另一组字节上。我是从同一个文件里读数据,然后再写回去。我的问题是,每次这样做的时候,文件里总会多出一些字节,除非我在关闭文件之前先移动到文件的末尾。下面是我的代码:
with open(self._path,'r+b') as source:
for lookAt in range(0,self._size[1]*self._size[2],1):
source.seek(lookAt*self._size[0],0)
readBuffer = array.array('B')
readBuffer.fromfile(source, self._size[0])
newLine = array.array('B',[mappingDict[mat] for mat in readBuffer])
source.seek(lookAt*self._size[0],0)
newLine.tofile(source)
source.seek(0,2) # Magic line that solves stupid bug
source.close()
我使用数组模块来读写数据,因为我在用read()和write()的时候也遇到了同样的问题。我不明白为什么那条被称为“魔法行”的代码能解决这个问题,因为我从来没有用过它。我希望能得到一些关于这个问题的见解。
2 个回答
1
我稍微试了一下这个问题,我猜这可能是Python 3里的一个bug。
为了支持我的猜测,我提供了以下代码(基于@J.F. Sebastian的代码):
import os
import sys
filename = '/tmp/a'
with open(filename, 'wb') as f:
f.write(b'1234a67b8ca')
print(open(filename, 'rb').read())
bufsize = 3
with open(filename, 'r+b') as f:
for i in range(0, os.path.getsize(filename), bufsize):
f.seek(i, os.SEEK_SET)
b = f.read(bufsize)
f.seek(i, os.SEEK_SET)
f.write(b)
# f.seek(0, os.SEEK_END) # magic
print(open(filename, 'rb').read())
在使用Python 2.7.1运行时,它的表现和你预期的一样,那个神奇的代码行没有影响。
在使用Python 3.1.2运行时,它莫名其妙地需要那个神奇的无操作 seek()
才能按预期工作。
在这个时候,我建议把代码展示给核心的Python 3开发者,听听他们的看法,看看这是否真的是个bug。
3
评论(答案在后面):
我看到的情况和你一样:
#!/usr/bin/env python3
import os
import sys
filename = '/tmp/a'
with open(filename, 'wb') as f:
f.write(b'1234a67b8ca')
print(open(filename, 'rb').read())
bufsize = 3
table = bytes.maketrans(b'abcde', b'xyzzz') # mapping
with open(filename, 'r+b') as f:
for i in range(0, os.path.getsize(filename), bufsize):
f.seek(i, os.SEEK_SET)
b = f.read(bufsize) # result shouldn't depend on it due to 1 -> 1
if not b:
break
f.seek(i, os.SEEK_SET)
f.write(b.translate(table))
f.seek(0, os.SEEK_END) # magic
print(open(filename, 'rb').read())
输出(使用魔法行或设置buffering=0,或者在f.write后调用f.flush)
b'1234a67b8ca'
b'1234x67y8zx'
输出(没有魔法行)
b'1234a67b8ca'
b'1234a67b8zx1234x67y8'
答案:
如果你的映射是1对1的关系,你可以使用 bytes.translate()
:
#!/usr/bin/env python3
import io
import os
import sys
filename = '/tmp/a'
data = b'1234a67b8ca'*10000
with open(filename, 'wb') as f:
f.write(data)
assert data == open(filename, 'rb').read()
print(data[:10]+data[-10:])
bufsize = io.DEFAULT_BUFFER_SIZE
table = bytes.maketrans(b'abcde', b'xyzzz') # mapping
with open(filename, 'r+b') as f:
while True:
b = f.read(bufsize) # result shouldn't depend on bufsize due to 1 -> 1
if not b:
break
f.seek(-len(b), os.SEEK_CUR)
f.write(b.translate(table))
f.flush()
tr_data = data.translate(table)
assert tr_data == open(filename, 'rb').read()
print(tr_data[:10]+tr_data[-10:])
看起来 io.BufferedRandom 在没有调用 flush()
的情况下,无法进行 交错的读/寻址/写入(这是Python3中的一个bug)。