【问题标题】:Python3 (v3.2.2) extra bits when writing binary files编写二进制文件时的 Python3 (v3.2.2) 额外位
【发布时间】:2011-10-26 06:14:53
【问题描述】:

我一直在研究将二进制文件的字节映射到另一组字节的函数。我正在读取和写入同一个文件。我的问题是每次我这样做时都会得到额外的字节,除非我在关闭文件之前移动到文件的末尾,这是我的代码:

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() 时遇到了同样的问题。我不明白为什么“魔术线”可以解决问题,因为它从未使用过。我会很感激我能得到的任何见解。

【问题讨论】:

  • 映射字典中有什么?字节或 unicode / 字符串?另外,_size 012 中的内容是什么?
  • 因为你使用的是with,所以最后一行source.close()是不必要的。

标签: python python-3.x binaryfiles


【解决方案1】:

评论(答案如下):

我看到和你一样的行为:

#!/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())

输出(在 f.write 之后使用幻线或 buffering=0 或 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:])

如果没有flush()io.BufferedRandom 似乎无法执行interlaced read/seek/write(Python3 中的错误)。

【讨论】:

  • 我不确定这是否能回答问题——为什么?但是,您是否使用我的代码得到相同的结果?
  • (+1) 不厌其烦地复制而不是猜测原因。
  • @agf 如果您的代码使用io.BufferedRandom() 的交错读/写(带有默认参数的“r+b”返回),那么同样的错误也适用。
  • 感谢您抽出宝贵时间。这很有帮助,现在我知道我不会发疯。顺便说一句,我避免直接​​使用字节的另一个原因是我记得读过 b'14' 字符串类型将在未来的 python 版本中被删除,我的代码需要永远工作。谢谢。
  • @user1013993:您可能将它与u'' 语法混淆了。由于"" 字符串在 Python3 中是 Unicode,它在 Python3 中被删除。你在哪里读到的?
【解决方案2】:

对此进行了一些实验,我推测这是 Python 3 中的一个错误。

为了支持我的猜想,我提供以下代码(基于@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 运行时,它莫名其妙地需要神奇的 no-op seek() 才能使其按预期工作。

在这一点上,我建议向核心 Python 3 开发人员演示代码,以获得他们对这是否确实是一个错误的意见。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多