Converter o texto binário para o 'bytearray' no Python 3

Apesar das muitas perguntas relacionadas, não encontro nada que corresponda ao meu problema. Eu gostaria de mudar uma string binária (por exemplo, "0110100001101001") para uma matriz de bytes (mesmo exemplo, b"hi").

eu tentei isto:

bytes([int(i) for i in "0110100001101001"])
Mas eu tenho ...
b'\x00\x01\x01\x00\x01' #... and so on
Qual é a maneira correcta de fazer isto no Python 3?

Author: Numeri, 2015-09-20

3 answers

Aqui está um exemplo de fazer isso da primeira maneira que Patrick mencionou: converter o bitstring para um int e tomar 8 bits de cada vez. A maneira natural de fazer isso gera os bytes em ordem inversa. Para obter os bytes de volta para a ordem adequada, eu uso notação de fatia estendida no bytearray com um passo de -1: b[::-1].
def bitstring_to_bytes(s):
    v = int(s, 2)
    b = bytearray()
    while v:
        b.append(v & 0xff)
        v >>= 8
    return bytes(b[::-1])

s = "0110100001101001"
print(bitstring_to_bytes(s))
Claramente, a segunda via do Patrick é mais compacta. :)

No entanto, há uma maneira melhor de fazer isto no Python 3: use o int. to_ bytes método:

def bitstring_to_bytes(s):
    return int(s, 2).to_bytes(len(s) // 8, byteorder='big')
 5
Author: PM 2Ring, 2015-09-20 06:45:49
>>> zero_one_string = "0110100001101001"
>>> int(zero_one_string, 2).to_bytes((len(zero_one_string) + 7) // 8, 'big')
b'hi'

Devolve bytes o objecto que é uma sequência imutável de bytes. Se você quiser obter um bytearray -- uma sequência mutável de bytes -- então basta ligar para bytearray(b'hi').

 5
Author: jfs, 2015-09-20 18:54:17

Você tem que convertê-lo em um int e tomar 8 bits de cada vez, ou cortá-lo em cordas de 8 bytes longos e, em seguida, converter cada um deles em ints. No Python 3, como as respostas de PM 2Ring e J. F Sebastian mostram, o método to_bytes() de int permite que você faça o primeiro método de forma muito eficiente. Isso não está disponível no Python 2, então para as pessoas presas com isso, o segundo método pode ser mais eficiente. Aqui está um exemplo:

>>> s = "0110100001101001"
>>> bytes(int(s[i : i + 8], 2) for i in range(0, len(s), 8))
b'hi'

Para quebrar isto, o intervalo começa em índice 0, e nos dá índices na cadeia de fonte, mas avança 8 índices de cada vez. Uma vez que s tem 16 caracteres de comprimento, vai dar - nos dois índices:

>>> list(range(0, 50, 8))
[0, 8, 16, 24, 32, 40, 48]
>>> list(range(0, len(s), 8))
[0, 8]

(usamos list() aqui para mostrar os valores que serão recuperados a partir do iterador de gama no Python 3.)

[[11]} Nós podemos então construir sobre isso para quebrar a cadeia separando-a com fatias que têm 8 caracteres de comprimento:
>>> [s[i : i + 8] for i in range(0, len(s), 8)]
['01101000', '01101001']
Então podemos converter cada um deles em inteiros, base 2:
>>> list(int(s[i : i + 8], 2) for i in range(0, len(s), 8))
[104, 105]
E finalmente ... , embrulhamos tudo em {[9] } para obter a resposta:
>>> bytes(int(s[i : i + 8], 2) for i in range(0, len(s), 8))
b'hi'
 4
Author: Patrick Maupin, 2015-09-20 19:09:24