python二进制字符串到二进制数据

时间:2014-01-03 12:26:36

标签: python

我的霍夫曼编码项目有问题。

我有一个文件的二进制表示字符串,但逻辑上它比我保存为文本文件时的原始文件更大。我想要的是将文件保存为二进制文件。

示例:在霍夫曼编码之后,让b c和d由以下“二进制代码”表示

a="0010" b="010" c="110" d="101"

因此,文件abcd的文件由二进制= "0010010110101"

表示

如果我将连接的二进制表示字符串保存为普通文本文件,则它大于原始abcd

但是我需要将二进制连接文件保存为实际二进制文件,该文件已经降低了大小 - 对于示例abcd= 8bit*4 = 32 bits最初,但之后,我需要它为13位。

我在python中这样做。

2 个答案:

答案 0 :(得分:2)

import struct
with open("foo.bin", 'wb') as f:
    f.write(struct.pack('h', 0b0010010110101))

将2个字节(16位)作为短整数(h)。您可以使用struct module定义自己的格式字符串,但我不确定您是否能够获得字节大小。

修改

根据你的评论,这里有一些背景知识:

在文件中写入内容时,它总是转换为二进制文件。字符使用一些规则编码,称为编码(例如ASCII),其中每个字符都映射到一个数字,本身以二进制表示。这样,数字00100100(36)和字符'$'是相同的东西。 '$'在文件上用36表示,你之间的软件层(例如编辑器)会将它遇到的每个'00100100'渲染为字符'$'。

现在,当您将字符串'00100100'写入文件时,它将打印字符'0','1'等....所以字符串 “00100100”由在二进制数 110000110000110001110000110000110001110000110000.因为输入是一个字符串,这是必要的表示,则需要代表所有可能的8个字符长串,不仅代表0的那些的明确的方式和1秒。

用于编写文件的Python API总是编写 strings ,即它将执行此转换字符串 - >自动二进制数,我不知道如何覆盖它。但是你可以做的是生成字符串,使的二进制表示形式是你想要写的实际二进制字符串:如果你想在文件中写入数字 00100100,你可以写f.write('$'),这实际上是一样的。

这正是'struct'模块执行的操作:它生成一个字节或字符串,与您提供的字符完全匹配。

在我的例子中,我给它编号为0b0010010110101,并告诉它将其编码为short整数,即两个字节。如果在Python解释器中执行struct.pack('h', 1205),它将打印出两个字符(字节)\xb5\x04,它们对应于'byte-base'中的这个数字,即base 256(具有big-endian约定) 。事实上:

>>> 0x04 * 256 + 0xb5
1205

就像您可以表示基数10(例如36),基数16(例如0x24),基数2(例如0b100100)中的任何十进制数一样,您也可以通过ASCII编码在基数256中表示它(例如'$' )。 Struct完全相同,也为您正在编写的数据类型提供了方便的“fmt”字符串约定。您也可以通过将每个字节转换为相应的字符来直接执行此操作:

def encode(binary):
    # Aligning on bytes
    binary = '0' * (8 - len(binary) % 8) + binary
    # Generating the corresponding character for each
    # byte encountered
    return ''.join(chr(int('0b' + binary[i:i+8], base = 2)) 
                   for i in xrange(0, len(binary), 8))

这是一种非常粗糙且不是非常有效的处理方式,但它确实将每个字节转换为相应的字符,并返回相应的字符串,您可以直接将其写入文件:

>>> encode('001001001010100100100100100111110010101110100')
'\x04\x95$\x93\xe5t'

实际上,将其写入文件会产生6个字节,对应于6个字符:

with open("foo.bin", 'wb') as f:
    f.write('\x04\x95$\x93\xe5t')

>>> os.path.getsize("foo.bin")
6L

struct模块执行完全相同的操作,除了固定格式,并且以更有效的方式。

,而不是获得与整数相对应的chr
def encode2(binary):
    rawbytes = []
    while binary > 0:
        binary, byte = divmod(binary, 256)
        rawbytes.append(byte)
    fmt_string = '%sB' % len(rawbytes)
    print "Encoding %s into %s bytes (%s)" % (rawbytes, len(rawbytes), fmt_string)
    return struct.pack(fmt_string, *rawbytes)

>>> encode2(0b001001001010100100100100100111110010101110100)
Encoding [116L, 229L, 147L, 36L, 149L, 4L] into 6 bytes (6B)
't\xe5\x93$\x95\x04'

(请注意,这些字符与encode中输出的字符相同。唯一的区别是顺序,具体取决于转换的字节顺序。)

然后,您也可以使用struct解码这些字符,并使用相同的格式字符串:

>>> bytes = struct.unpack('6B', 't\xe5\x93$\x95\x04')
>>> bytes
(116, 229, 147, 36, 149, 4)
>>> bin(sum(x * 256 ** i for i, x in enumerate(bytes)))
'0b1001001010100100100100100111110010101110100'

这是我们的原始号码。

底线是:Python文件API只能处理字符,它们实际上是字节。可能有一些神奇的方法可以将单个位写入文件,但我不会过多考虑,因为这引入了自己的问题世界,99%的情况下字节数已经足够了。要写入二进制数据,请将其表示为256,并将其每个b256数字转换为相应的字符。根据定义,此字符串的二进制表示形式是您的原始数字。

答案 1 :(得分:0)

binascii可以使用。

import binascii

a = "1010"
b = "10"
c = "00"

data = a + b + c
hex_string = hex(int(data, 2))[2:]  #remove '0x'

with open('foo', 'wb') as f:
    f.write(binascii.unhexlify(hex_string))

hex_string应该是均匀的,因此您需要向"0010010110101"添加一位才能使unhexlify正常工作。