【问题标题】:Write boolean string to binary file?将布尔字符串写入二进制文件?
【发布时间】:2012-09-22 05:40:49
【问题描述】:

我有一串布尔值,我想用这些布尔值作为位来创建一个二进制文件。这就是我正在做的:

# first append the string with 0s to make its length a multiple of 8
while len(boolString) % 8 != 0:
    boolString += '0'

# write the string to the file byte by byte
i = 0
while i < len(boolString) / 8:
    byte = int(boolString[i*8 : (i+1)*8], 2)
    outputFile.write('%c' % byte)

    i += 1

但这一次生成输出 1 个字节并且速度很慢。什么是更有效的方法?

【问题讨论】:

标签: python file-io binary binaryfiles


【解决方案1】:

如果您先计算所有字节然后将它们全部写入,应该会更快。例如

b = bytearray([int(boolString[x:x+8], 2) for x in range(0, len(boolString), 8)])
outputFile.write(b)

我也在使用bytearray,这是一个自然的容器,也可以直接写入你的文件。


如果合适,您当然可以使用库,例如​​ bitarraybitstring。使用后者你可以说

bitstring.Bits(bin=boolString).tofile(outputFile)

【讨论】:

    【解决方案2】:

    这是另一个答案,这次使用来自 PyCrypto - The Python Cryptography Toolkit 的工业级实用函数,在 2.6 版(当前最新的稳定版本)中,它在pycrypto-2.6/lib/Crypto/Util/number.py 中定义。

    前面的 cmets 说:
    Improved conversion functions contributed by Barry Warsaw, after careful benchmarking

    import struct
    
    def long_to_bytes(n, blocksize=0):
        """long_to_bytes(n:long, blocksize:int) : string
        Convert a long integer to a byte string.
    
        If optional blocksize is given and greater than zero, pad the front of the
        byte string with binary zeros so that the length is a multiple of
        blocksize.
        """
        # after much testing, this algorithm was deemed to be the fastest
        s = b('')
        n = long(n)
        pack = struct.pack
        while n > 0:
            s = pack('>I', n & 0xffffffffL) + s
            n = n >> 32
        # strip off leading zeros
        for i in range(len(s)):
            if s[i] != b('\000')[0]:
                break
        else:
            # only happens when n == 0
            s = b('\000')
            i = 0
        s = s[i:]
        # add back some pad bytes.  this could be done more efficiently w.r.t. the
        # de-padding being done above, but sigh...
        if blocksize > 0 and len(s) % blocksize:
            s = (blocksize - len(s) % blocksize) * b('\000') + s
        return s
    

    【讨论】:

      【解决方案3】:

      您可以使用data = long(boolString,2) 将布尔字符串转换为long。然后将此长写入磁盘,您可以使用:

      while data > 0:
          data, byte = divmod(data, 0xff)
          file.write('%c' % byte)
      

      但是,没有必要创建一个布尔字符串。使用long 要容易得多。 long 类型可以包含无限位数。使用位操作,您可以根据需要设置或清除位。然后,您可以在一次写入操作中将 long 整体写入磁盘。

      【讨论】:

      • 使用long 的问题是它忽略了任何前导0 位。
      • 视情况而定。如果读取文件的程序知道布尔字符串的长度,这不是问题。情况似乎是这样,否则,读取程序如何知道要读取多少字节。
      • 它要么从文件中获取长度并读取所有文件,要么提前知道要读取多少字节。如果它读取了所有文件,那么我的论点就成立,否则 long 的问题是程序必须知道它存储了多少字节,以及在重新插入前导零之后它将是多少字节。您基本上必须将长度与 long 一起存储,这是一个简单的编解码器,而不仅仅是存储数据。
      • 如果它读取了所有文件,那也没关系,因为无论您读取多少字节,数据都是相同的。
      • 它不会从文件中读取所有内容。但是文件中有一个标头指示要读取多少位。
      【解决方案4】:

      您可以使用array 类尝试此代码:

      import array
      
      buffer = array.array('B')
      
      i = 0
      while i < len(boolString) / 8:
          byte = int(boolString[i*8 : (i+1)*8], 2)
          buffer.append(byte)
          i += 1
      
      f = file(filename, 'wb')
      buffer.tofile(f)
      f.close()
      

      【讨论】:

        【解决方案5】:

        helper class(如下所示)让事情变得简单:

        class BitWriter:
            def __init__(self, f):
                self.acc = 0
                self.bcount = 0
                self.out = f
        
            def __del__(self):
                self.flush()
        
            def writebit(self, bit):
                if self.bcount == 8 :
                    self.flush()
                if bit > 0:
                    self.acc |= (1 << (7-self.bcount))
                self.bcount += 1
        
            def writebits(self, bits, n):
                while n > 0:
                    self.writebit( bits & (1 << (n-1)) )
                    n -= 1
        
            def flush(self):
                self.out.write(chr(self.acc))
                self.acc = 0
                self.bcount = 0
        
        with open('outputFile', 'wb') as f:
            bw = BitWriter(f)
            bw.writebits(int(boolString,2), len(boolString))
            bw.flush()
        

        【讨论】:

          【解决方案6】:

          使用struct package

          这可用于处理存储在文件中或来自网络连接以及其他来源的二进制数据。

          编辑:

          使用? 作为format character 用于bool 的示例。

          import struct
          
          p = struct.pack('????', True, False, True, False)
          assert p == '\x01\x00\x01\x00'
          with open("out", "wb") as o:
              o.write(p)
          

          我们来看看文件:

          $ ls -l out
          -rw-r--r-- 1 lutz lutz 4 Okt  1 13:26 out
          $ od out
          0000000 000001 000001
          000000
          

          再读一遍:

          with open("out", "rb") as i:
              q = struct.unpack('????', i.read())
          assert q == (True, False, True, False)
          

          【讨论】:

          • 很抱歉,但我不确定我是否跟随。我查找了结构,它用于根据某种格式打包/解包。但是我没有格式。它们是原始位,我想按原样将它们放入文件中。
          • 不 \x00 \x01 每个代表一个字节吗?但我希望 true 生成 1 位。所以我的输出文件应该是10100000(最后四位是pad)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-08-28
          • 2021-10-27
          • 2016-01-15
          • 2016-10-03
          • 2010-10-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多