преобразование строки байтов в int (python)
как я могу преобразовать строку байтов в int в Python?
скажем, так: 'yxccxa6xbb'
Я придумал умный/глупый способ сделать это:
sum(ord(c) << (i * 8) for i, c in enumerate('yxccxa6xbb'[::-1]))
Я знаю, что должно быть что-то встроенное или в стандартной библиотеке, что делает это более просто...
это отличается от преобразование строки шестнадцатеричных цифр для которого вы можете использовать int (xxx, 16), но вместо этого я хочу преобразовать строку фактического байта ценности.
обновление:
мне нравится ответ Джеймса немного лучше, потому что он не требует импорта другого модуля, но метод Грега быстрее:
>>> from timeit import Timer
>>> Timer('struct.unpack("<L", "yxccxa6xbb")[0]', 'import struct').timeit()
0.36242198944091797
>>> Timer("int('yxccxa6xbb'.encode('hex'), 16)").timeit()
1.1432669162750244
мой хакерский метод:
>>> Timer("sum(ord(c) << (i * 8) for i, c in enumerate('yxccxa6xbb'[::-1]))").timeit()
2.8819329738616943
УТОЧНЕНИЯ:
кто-то спросил в комментариях, в чем проблема с импортом другого модуля. Ну, импорт модуля не обязательно дешево, посмотрите:
>>> Timer("""import structnstruct.unpack(">L", "yxccxa6xbb")[0]""").timeit()
0.98822188377380371
включая стоимость импорта модуля отрицает практически все преимущества, которые имеет этот метод. Я считаю, что это будет включать только расходы на импорт его один раз за весь тестовый запуск; посмотрите, что происходит, когда я заставляю его перезагружать каждый раз:
>>> Timer("""reload(struct)nstruct.unpack(">L", "yxccxa6xbb")[0]""", 'import struct').timeit()
68.474128007888794
Излишне говорить, что если вы делаете много выполнения этого метода за один импорт, чем это становится пропорционально меньше проблем. Это также, вероятно, стоимость ввода / вывода, а не процессор, поэтому она может зависеть от емкости и характеристик нагрузки конкретного машина.
10 ответов:
вы также можете использовать 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-целых чисел произвольной длины, а для двух-дополняют целые числа со знаком, указав
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 для списка спецификаторов формата.
int.from_bytes-лучшее решение, если вы находитесь в версии >=3.2. В "структуре.решение "распаковать" требует строки, поэтому оно не будет применяться к массивам байтов. Вот еще одно решение:
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 байт
>>> 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, как и ожидалось.
Как упоминалось выше, используя
unpack
функции struct - это хороший способ. Если вы хотите реализовать свою собственную функцию, есть другое решение:def bytes_to_int(bytes): result = 0 for b in bytes: result = result * 256 + int(b) return result
Я изо всех сил пытался найти решение для последовательностей байтов произвольной длины, которые будут работать под Python 2.x. наконец, я написал это, это немного хаки, потому что он выполняет преобразование строк, но он работает.
функция для 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)
данные должны быть big-endian. Если у вас есть значение little-endian, вы должны сначала отменить его:
n = signedbytes(s[::-1])
конечно, это должно использоваться только в том случае, если требуется произвольная длина. В противном случае придерживайтесь более стандартных способов (например,
struct
).