二进制转字符串,比字典更好吗?

6

目标:将二进制转换为字符串

示例:0111010001100101011100110111010001100011011011110110010001100101 -> testCode(不带空格)

我使用一个字典和我的函数,我在寻找更好的方法并提高效率。

from textwrap import wrap

DICO = {'\x00': '00', '\x04': '0100', '\x08': '01000', '\x0c': '01100', 
'\x10': '010000', '\x14': '010100', '\x18': '011000', '\x1c': '011100',
' ': '0100000', '$': '0100100', '(': '0101000', ',': '0101100', '0': '0110000',
'4': '0110100', '8': '0111000', '<': '0111100', '@': '01000000',
'D': '01000100', 'H': '01001000', 'L': '01001100', 'P': '01010000',
'T': '01010100', 'X': '01011000', '\\': '01011100', '`': '01100000',
'd': '01100100', 'h': '01101000', 'l': '01101100', 'p': '01110000',
't': '01110100', 'x': '01111000', '|': '01111100', '\x03': '011',
'\x07': '0111', '\x0b': '01011', '\x0f': '01111', '\x13': '010011',
'\x17': '010111', '\x1b': '011011', '\x1f': '011111', '#': '0100011',
"'": '0100111', '+': '0101011', '/': '0101111', '3': '0110011', '7': '0110111',
';': '0111011', '?': '0111111', 'C': '01000011', 'G': '01000111',
'K': '01001011', 'O': '01001111', 'S': '01010011', 'W': '01010111',
'[': '01011011', '_': '01011111', 'c': '01100011', 'g': '01100111',
'k': '01101011', 'o': '01101111', 's': '01110011', 'w': '01110111',
'{': '01111011', '\x7f': '01111111', '\x02': '010', '\x06': '0110',
'\n': '01010', '\x0e': '01110', '\x12': '010010', '\x16': '010110',
'\x1a': '011010', '\x1e': '011110', '"': '0100010', '&': '0100110',
'*': '0101010', '.': '0101110', '2': '0110010', '6': '0110110', ':': '0111010',
'>': '0111110', 'B': '01000010', 'F': '01000110', 'J': '01001010',
'N': '01001110', 'R': '01010010', 'V': '01010110', 'Z': '01011010',
'^': '01011110', 'b': '01100010', 'f': '01100110', 'j': '01101010',
'n': '01101110', 'r': '01110010', 'v': '01110110', 'z': '01111010',
'~': '01111110', '\x01': '01', '\x05': '0101', '\t': '01001', '\r': '01101',
'\x11': '010001', '\x15': '010101', '\x19': '011001', '\x1d': '011101',
'!': '0100001', '%': '0100101', ')': '0101001', '-': '0101101',
'1': '0110001', '5': '0110101', '9': '0111001', '=': '0111101',
'A': '01000001', 'E': '01000101', 'I': '01001001', 'M': '01001101',
'Q': '01010001', 'U': '01010101', 'Y': '01011001', ']': '01011101',
'a': '01100001', 'e': '01100101', 'i': '01101001', 'm': '01101101',
'q': '01110001', 'u': '01110101', 'y': '01111001', '}': '01111101'}

def decrypt(binary):
    """Function to convert binary into string"""
    binary = wrap(binary, 8)
    ch = ''
    for b in binary:
        for i, j in DICO.items():
            if j == b:
                ch += i
    return ch

提前感谢您。

1
你到底想要在哪方面进行改进?是空间复杂度还是时间复杂度?你目前使用的算法是什么,仅仅是把字符串分割吗? - RedX
6
如何确定序列0100010100(=\x04)后跟着一个01序列,还是哈希标记字符 0100011的开头?如果所有字符都有相同位数(假定为7或8位),为什么\x00的代码在你的示例中不写成00000000而是00?这个示例数据留下了很多解释的空间... - hochl
@Redx,谢谢你的回复,我在寻找更好的执行时间,否则我将保留我的代码。 - Fred
这段代码的一个奇怪之处在于它永远不会匹配短项,比如 '\x00': '00'。 - Xavier Combelle
3个回答

14
''.join([ chr(int(p, 2)) for p in wrap(binstr, 8) ])

这段代码的作用是:wrap 首先将字符串分成长度为8的块。然后,我遍历每个块,将它们转换为二进制整数。每个转换后的整数现在都被转换为字符,并使用 chr 函数进行转换。最后,使用 ''.join 将所有字符连接在一起。

下面更详细地解释 chr(int(p, 2)) 的每个步骤:

>>> int('01101010', 2)
106
>>> chr(106)
'j'
让它符合您上面所述的模式:
def decrypt(binary):
    """Function to convert binary into string"""
    binary = wrap(binary, 8)
    ch = ''
    for b in binary:
        ch += chr(int(b, 2))
    return ch
或者
def decrypt(binary):
    """Function to convert binary into string"""
    return ''.join([ chr(int(p, 2)) for p in wrap(binary, 8) ])

这绝对更快,因为它只是在原地进行数学计算,而不是一遍又一遍地遍历字典。此外,它更易读。


目前来看,它们是等价的! - Fred
你的字符串有多大?可能是wrap占用了大部分时间,这方面你无能为力。不管怎样,这仍然比那个可怕的字典要易读得多。 - Donald Miner
我不知道您所说的“wrap”,但是使用长度为5000的字典只需1.94秒,而您的代码需要3.45秒。 - Fred
@orangeoctopus 或许值得用可迭代的等价物 (''.join(i) for i in grouper(8,binary,'')) 替换 wrap(binary, 8),使用来自 这里grouper?甚至可以用整个字符串替换为 ''.join([ chr(int(''.join(p), 2)) for p in grouper(8,s)]) - ovgolovin
我也在想这个。或许楼主想要尝试一下。 - Donald Miner

3
如果执行速度对您来说最重要,为什么不颠倒字典中的键和值?!(如果您还需要当前的字典,可以创建一个反转版本,如此:{v:k for k, v in DICO.items()}
现在,您可以直接通过键找到所需的翻译,而不必遍历整个字典。
您的新函数应该像这样:
def decrypt2(binary):
    """Function to convert binary into string"""
    binary = wrap(binary, 8)
    ch = ''
    for b in binary:
        if b in DICO_INVERTED:
            ch += DICO_INVERTED[b]
    return ch

根据您的二进制字符串大小,通过更改构建输出字符串的方式可能会节省一些时间(请参见Python中的高效字符串连接性能提示-字符串连接)。使用join方法似乎是很有前途的。 我建议试试这种方式:''.join(DICO_INVERTED.get(b, '') for b in binary)


1
我建议使用代码创建字典,而不是手动输入。 - Donald Miner
@orangeoctopus,我不想打字典 ;) - Fred
@user1125315 你尝试过使用 join 吗?我对你能获得的性能提升(或者没有提升)很感兴趣... - gecco

2
你尝试过了吗?
def decrypt(binary):
    """Function to convert binary into string"""
    return ''.join(( chr(int(p, 2)) for p in grouper(8,binary,'') ))

grouper是从这里获取的:http://docs.python.org/library/itertools.html#recipes

或者

def decrypt2(binary):
    """Function to convert binary into string"""
    return ''.join(( DICO_INVERTED[p] for p in grouper(8,binary,'') ))

避免创建临时列表

编辑 因为我被选择为“正确”答案,所以必须坦白我使用了其他答案。重点在于不使用生成器列表,而是使用生成器表达式和迭代器。


你测试的是带字典的那个还是另外一个? - Xavier Combelle
平均需要14秒,而您的提议只需要0.006秒,超级棒,非常感谢。 - Fred

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接