Мне нужно преобразовать строку ASCII в список бит и наоборот:
str = "Hi" -> [0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1]
[0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1] -> "Hi"
Мне нужно преобразовать строку ASCII в список бит и наоборот:
str = "Hi" -> [0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1]
[0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1] -> "Hi"
Есть, вероятно, более быстрые способы сделать это, но без дополнительных модулей:
def tobits(s):
result = []
for c in s:
bits = bin(ord(c))[2:]
bits = '00000000'[len(bits):] + bits
result.extend([int(b) for b in bits])
return result
def frombits(bits):
chars = []
for b in range(len(bits) / 8):
byte = bits[b*8:(b+1)*8]
chars.append(chr(int(''.join([str(bit) for bit in byte]), 2)))
return ''.join(chars)
Существует много способов сделать это с помощью библиотечных функций. Но я частично отношусь к стороннему bitarray
.
>>> import bitarray
>>> ba = bitarray.bitarray()
Преобразование из строк требует некоторой церемонии. Когда-то вы могли просто использовать fromstring
, но этот метод теперь устарел, поскольку он должен неявно кодировать строку в байты. Чтобы избежать неизбежных ошибок кодирования, лучше передать объект bytes
на frombytes
. Когда вы начинаете с строки, это означает, что вы должны явно указывать кодировку - что является хорошей практикой в любом случае.
>>> ba.frombytes('Hi'.encode('utf-8'))
>>> ba
bitarray('0100100001101001')
Преобразование в список легко. (Кроме того, объекты bitstring имеют множество спискообразных функций.)
>>> l = ba.tolist()
>>> l
[False, True, False, False, True, False, False, False,
False, True, True, False, True, False, False, True]
bitstring
может быть создан из любого итерабельного:
>>> bitarray.bitarray(l)
bitarray('0100100001101001')
Преобразование обратно в байты или строки относительно просто:
>>> bitarray.bitarray(l).tobytes().decode('utf-8')
'Hi'
И ради чистого развлечения:
>>> def s_to_bitlist(s):
... ords = (ord(c) for c in s)
... shifts = (7, 6, 5, 4, 3, 2, 1, 0)
... return [(o >> shift) & 1 for o in ords for shift in shifts]
...
>>> def bitlist_to_chars(bl):
... bi = iter(bl)
... bytes = zip(*(bi,) * 8)
... shifts = (7, 6, 5, 4, 3, 2, 1, 0)
... for byte in bytes:
... yield chr(sum(bit << s for bit, s in zip(byte, shifts)))
...
>>> def bitlist_to_s(bl):
... return ''.join(bitlist_to_chars(bl))
...
>>> s_to_bitlist('Hi')
[0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]
>>> bitlist_to_s(s_to_bitlist('Hi'))
'Hi'
не уверен, почему, но вот два уродливых oneliners, использующих только встроенные функции:
s = "Hi"
l = map(int, ''.join([bin(ord(i)).lstrip('0b').rjust(8,'0') for i in s]))
s = "".join(chr(int("".join(map(str,l[i:i+8])),2)) for i in range(0,len(l),8))
дает:
>>> l
[0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]
>>> s
'Hi'
В коде реального мира используйте модуль struct
или bitarray
.
Вы можете использовать встроенный bytearray
:
>>> for i in bytearray('Hi', 'ascii'):
... print(i)
...
72
105
>>> bytearray([72, 105]).decode('ascii')
'Hi'
И bin()
для преобразования в двоичный файл.
def text_to_bits(text):
"""
>>> text_to_bits("Hi")
[0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]
"""
bits = bin(int.from_bytes(text.encode(), 'big'))[2:]
return list(map(int, bits.zfill(8 * ((len(bits) + 7) // 8))))
def text_from_bits(bits):
"""
>>> text_from_bits([0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1])
'Hi'
"""
n = int(''.join(map(str, bits)), 2)
return n.to_bytes((n.bit_length() + 7) // 8, 'big').decode()
См. также Преобразование двоичных файлов в ASCII и наоборот (Python).
Несколько скоростных сравнений. Каждый из них запускался с использованием
python -m timeit "code"
или
cat <<-EOF | python -m timeit
code
EOF
если многострочный.
A: 100000000 циклов, лучше всего 3: 0.00838 usec за цикл
res = 0
for idx,x in enumerate([0,0,1,0,1,0,0,1]):
res |= (x << idx)
B: 100000000 циклов, лучше всего 3: 0.00838 usec за цикл
int(''.join(map(str, [0,0,1,0,1,0,0,1])), 2)
A: 100000000 циклов, лучше всего 3: 0.00836 usec за цикл
[(41 >> x) & 1 for x in range(7, -1, -1)]
B: 100000 циклов, лучшее из 3: 2.07 usec за цикл
map(int, bin(41)[2:])
def to_bin(string):
res = ''
for char in string:
tmp = bin(ord(char))[2:]
tmp = '%08d' %int(tmp)
res += tmp
return res
def to_str(string):
res = ''
for idx in range(len(string)/8):
tmp = chr(int(string[idx*8:(idx+1)*8], 2))
res += tmp
return res
Эти функции действительно просты.
Он не использует сторонний модуль.
import math
class BitList:
def __init__(self, value):
if isinstance(value, str):
value = sum([bytearray(value, "utf-8")[-i - 1] << (8*i) for i in range(len(bytearray(value, "utf-8")))])
try:
self.value = sum([value[-i - 1] << i for i in range(len(value))])
except Exception:
self.value = value
def __getitem__(self, index):
if isinstance(index, slice):
if index.step != None and index.step != 1:
return list(self)[index]
else:
start = index.start if index.start else 0
stop = index.stop if index.stop != None else len(self)
return BitList(math.floor((self.value % (2 ** (len(self) - start))) >> (len(self) - stop)))
else:
return bool(self[index:index + 1].value)
def __len__(self):
return math.ceil(math.log2(self.value + 1))
def __str__(self):
return self.value
def __repr__(self):
return "BitList(" + str(self.value) + ")"
def __iter__(self):
yield from [self[i] for i in range(len(self))]
Затем вы можете инициализировать BitList
числом или списком (чисел или логических элементов), тогда вы можете получить его значение, получить позиционные элементы, получить фрагменты и преобразовать их в список. Примечание. В настоящее время невозможно установить элементы, но когда я добавлю, что я отредактирую это сообщение.
Я сделал это сам, затем пошел искать, как преобразовать строку (или файл) в список бит, а затем вычислил это из другого ответа.
Это может сработать, но это не сработает, если вы спросите PEP 8 (длинная строка, сложная)
tobits = lambda x: "".join(map(lambda y:'00000000'[len(bin(ord(y))[2:]):]+bin(ord(y))[2:],x))
frombits = lambda x: ''.join([chr(int(str(y), 2)) for y in [x[y:y+8] for y in range(0,len(x),8)]])
Они используются как обычные функции.
Поскольку мне нравятся генераторы, я отправлю свою версию здесь:
def bits(s):
for c in s:
yield from (int(bit) for bit in bin(ord(c))[2:].zfill(8))
def from_bits(b):
for i in range(0, len(b), 8):
yield chr(int(''.join(str(bit) for bit in b[i:i + 8]), 2))
print(list(bits('Hi')))
[0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]
print(''.join(from_bits([0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1])))
Hi