Ответ 1
Вы также можете использовать struct для этого:
>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L
Как преобразовать строку байтов в int в python?
Скажите так: 'y\xcc\xa6\xbb'
Я придумал умный/глупый способ сделать это:
sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))
Я знаю, что должно быть что-то встроенное или в стандартную библиотеку, которая делает это проще...
Это отличается от преобразование строки шестнадцатеричных цифр, для которой вы можете использовать int (xxx, 16), но вместо этого я хочу преобразовать строку фактического байта значения.
UPDATE:
Я вроде как ответ Джеймса немного лучше, потому что он не требует импорта другого модуля, но метод Грега выполняется быстрее:
>>> from timeit import Timer
>>> Timer('struct.unpack("<L", "y\xcc\xa6\xbb")[0]', 'import struct').timeit()
0.36242198944091797
>>> Timer("int('y\xcc\xa6\xbb'.encode('hex'), 16)").timeit()
1.1432669162750244
Мой взломанный метод:
>>> Timer("sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))").timeit()
2.8819329738616943
ДАЛЬНЕЙШЕЕ ОБНОВЛЕНИЕ:
Кто-то спросил в комментариях, что проблема с импортом другого модуля. Ну, импортировать модуль не обязательно дешево, посмотрите:
>>> Timer("""import struct\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""").timeit()
0.98822188377380371
Включение стоимости импорта модуля сводит на нет почти все преимущества этого метода. Я считаю, что это будет включать только расходы на импорт его один раз для всего эталонного теста; посмотрите, что произойдет, когда я заставляю его перезагружаться каждый раз:
>>> Timer("""reload(struct)\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""", 'import struct').timeit()
68.474128007888794
Излишне говорить, что если вы делаете много исполнений этого метода за один импорт, это становится менее пропорциональным. Это также, вероятно, стоимость ввода-вывода, а не процессора, поэтому он может зависеть от характеристик емкости и нагрузки конкретной машины.
Вы также можете использовать struct для этого:
>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L
В Python 3.2 и более поздних версиях используйте
>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='big')
2043455163
или
>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='little')
3148270713
в соответствии с endianness вашей байтовой строки.
Это также работает для bytestring-integers произвольной длины и для двухзначных целых чисел с помощью дополнения signed=True
. См. документы для from_bytes
.
Как сказал Грег, вы можете использовать struct, если имеете дело с двоичными значениями, но если у вас просто есть "шестнадцатеричное число", но в байтовом формате вы можете просто преобразовать его, как:
s = 'y\xcc\xa6\xbb'
num = int(s.encode('hex'), 16)
... это то же самое, что:
num = struct.unpack(">L", s)[0]
... кроме того, что он будет работать для любого количества байтов.
Я использую следующую функцию для преобразования данных между int, hex и байтами.
def bytes2int(str):
return int(str.encode('hex'), 16)
def bytes2hex(str):
return '0x'+str.encode('hex')
def int2bytes(i):
h = int2hex(i)
return hex2bytes(h)
def int2hex(i):
return hex(i)
def hex2int(h):
if len(h) > 1 and h[0:2] == '0x':
h = h[2:]
if len(h) % 2:
h = "0" + h
return int(h, 16)
def hex2bytes(h):
if len(h) > 1 and h[0:2] == '0x':
h = h[2:]
if len(h) % 2:
h = "0" + h
return h.decode('hex')
Источник: http://opentechnotes.blogspot.com.au/2014/04/convert-values-to-from-integer-hex.html
import array
integerValue = array.array("I", 'y\xcc\xa6\xbb')[0]
Предупреждение: приведенное выше строго зависит от платформы. И спецификатор "I", и сущность преобразования string- > int зависят от конкретной реализации Python. Но если вы хотите преобразовать сразу несколько целых чисел/строк, то модуль массива сделает это быстро.
В Python 2.x вы могли использовать спецификаторы формата <B
для неподписанных байтов и <b
для подписанных байтов с struct.unpack
/struct.pack
.
Например:
Пусть x
= '\xff\x10\x11'
data_ints = struct.unpack('<' + 'B'*len(x), x) # [255, 16, 17]
А также:
data_bytes = struct.pack('<' + 'B'*len(data_ints), *data_ints) # '\xff\x10\x11'
Это *
обязательно!
См. Https://docs.python.org/2/library/struct.html#format-characters для получения списка спецификаторов формата.
>>> reduce(lambda s, x: s*256 + x, bytearray("y\xcc\xa6\xbb"))
2043455163
Тест 1: обратный:
>>> hex(2043455163)
'0x79cca6bb'
Тест 2: количество байтов > 8:
>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAAA"))
338822822454978555838225329091068225L
Тест 3: Приращение на единицу:
>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAAB"))
338822822454978555838225329091068226L
Тест 4: добавьте один байт, скажем 'A':
>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))
86738642548474510294585684247313465921L
Тест 5: Разделить на 256:
>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))/256
338822822454978555838225329091068226L
Результат равен результату теста 4, как и ожидалось.
Я изо всех сил пытался найти решение для произвольных байтов длины, которые будут работать под Python 2.x. Наконец, я написал это, он немного взломан, потому что он выполняет преобразование строк, но он работает.
def signedbytes(data):
"""Convert a bytearray into an integer, considering the first bit as
sign. The data must be big-endian."""
negative = data[0] & 0x80 > 0
if negative:
inverted = bytearray(~d % 256 for d in data)
return -signedbytes(inverted) - 1
encoded = str(data).encode('hex')
return int(encoded, 16)
Эта функция имеет два требования:
Вход data
должен быть bytearray
. Вы можете вызвать функцию следующим образом:
s = 'y\xcc\xa6\xbb'
n = signedbytes(s)
Данные должны быть большими. В случае, если у вас есть значение little-endian, вы должны сначала отменить его:
n = signedbytes(s[::-1])
Конечно, это нужно использовать, только если требуется произвольная длина. В противном случае придерживайтесь более стандартных способов (например, struct
).
int.from_bytes - лучшее решение, если вы находитесь в версии >= 3.2. Для решения "struct.unpack" требуется строка, поэтому она не будет применяться к массивам байтов. Вот еще одно решение:
def bytes2int( tb, order='big'):
if order == 'big': seq=[0,1,2,3]
elif order == 'little': seq=[3,2,1,0]
i = 0
for j in seq: i = (i<<8)+tb[j]
return i
hex (bytes2int ([0x87, 0x65, 0x43, 0x21])) возвращает '0x87654321'.
Он обрабатывает большую и небольшую endianness и легко модифицируется для 8 байтов
Как уже упоминалось выше, использование функции unpack
структуры является хорошим способом. Если вы хотите реализовать свою собственную функцию, есть другое решение:
def bytes_to_int(bytes):
result = 0
for b in bytes:
result = result * 256 + int(b)
return result
Достаточно быстрый метод, использующий array.array, который я использовал в течение некоторого времени:
предопределенные переменные:
offset = 0
size = 4
big = True # endian
arr = array('B')
arr.fromstring("\x00\x00\xff\x00") # 5 bytes (encoding issues) [0, 0, 195, 191, 0]
в int: (читать)
val = 0
for v in arr[offset:offset+size][::pow(-1,not big)]: val = (val<<8)|v
из int: (написать)
val = 16384
arr[offset:offset+size] = \
array('B',((val>>(i<<3))&255 for i in range(size)))[::pow(-1,not big)]
Возможно, это может быть быстрее, хотя.
РЕДАКТИРОВАТЬ:
Для некоторых чисел здесь тест производительности (Anaconda 2.3.0), показывающий стабильные средние значения при чтении по сравнению с reduce()
:
========================= byte array to int.py =========================
5000 iterations; threshold of min + 5000ns:
______________________________________code___|_______min______|_______max______|_______avg______|_efficiency
⣿⠀⠀⠀⠀⡇⢀⡀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡀⠀⢰⠀⠀⠀⢰⠀⠀⠀⢸⠀⠀⢀⡇⠀⢀⠀⠀⠀⠀⢠⠀⠀⠀⠀⢰⠀⠀⠀⢸⡀⠀⠀⠀⢸⠀⡇⠀⠀⢠⠀⢰⠀⢸⠀
⣿⣦⣴⣰⣦⣿⣾⣧⣤⣷⣦⣤⣶⣾⣿⣦⣼⣶⣷⣶⣸⣴⣤⣀⣾⣾⣄⣤⣾⡆⣾⣿⣿⣶⣾⣾⣶⣿⣤⣾⣤⣤⣴⣼⣾⣼⣴⣤⣼⣷⣆⣴⣴⣿⣾⣷⣧⣶⣼⣴⣿⣶⣿⣶
val = 0 \nfor v in arr: val = (val<<8)|v | 5373.848ns | 850009.965ns | ~8649.64ns | 62.128%
⡇⠀⠀⢀⠀⠀⠀⡇⠀⡇⠀⠀⣠⠀⣿⠀⠀⠀⠀⡀⠀⠀⡆⠀⡆⢰⠀⠀⡆⠀⡄⠀⠀⠀⢠⢀⣼⠀⠀⡇⣠⣸⣤⡇⠀⡆⢸⠀⠀⠀⠀⢠⠀⢠⣿⠀⠀⢠⠀⠀⢸⢠⠀⡀
⣧⣶⣶⣾⣶⣷⣴⣿⣾⡇⣤⣶⣿⣸⣿⣶⣶⣶⣶⣧⣷⣼⣷⣷⣷⣿⣦⣴⣧⣄⣷⣠⣷⣶⣾⣸⣿⣶⣶⣷⣿⣿⣿⣷⣧⣷⣼⣦⣶⣾⣿⣾⣼⣿⣿⣶⣶⣼⣦⣼⣾⣿⣶⣷
val = reduce( shift, arr ) | 6489.921ns | 5094212.014ns | ~12040.269ns | 53.902%
Это необработанный тест производительности, так что endian pow-flip исключен.
Показанная функция shift
применяет ту же операцию сдвига, что и цикл for, а arr
- это просто array.array('B',[0,0,255,0])
как он имеет самую быструю итеративную производительность по dict
с dict
.
Я должен также отметить, что эффективность измеряется с точностью до среднего времени.
В Python 3 вы можете легко преобразовать строку байтов в список целых чисел (0..255) с помощью
>>> list(b'y\xcc\xa6\xbb')
[121, 204, 166, 187]