преобразование строки байтов в 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 122

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).