2009-01-14 2 views
107

Как преобразовать строку байтов в int в python?преобразовать строку байтов в int (python)

Скажем так: 'y\xcc\xa6\xbb'

я придумал умный/глупый способ сделать это:

sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1])) 

Я знаю, что там должно быть что-то встроенных или в стандартной библиотеке, что делает это более просто ...

Это отличается от converting a string of hex digits, для которого вы можете использовать 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 

Мой Hacky метод:

>>> Timer("sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))").timeit() 
2.8819329738616943 

ДАЛЕЕ UPDATE:

Кто-то спросил в комментариях, что это пр oblem с импортом другого модуля. Ну, импортируя модуль не обязательно дешевое, посмотрите:

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

Излишне говорить, что если вы делаете много выполнений этого метода на один импорт, чем это становится пропорционально меньше проблемы. Это также, вероятно, стоимость ввода-вывода, а не процессора, поэтому он может зависеть от характеристик емкости и нагрузки конкретной машины.

+0

и импортировать что-то из стандартной библиотеки - это плохо, почему? – hop

+0

andyway, duplicate: http://stackoverflow.com/questions/5415/convert-bytes-to-floating-point-numbers-in-python – hop

+19

ваше «дальнейшее обновление» странно ... почему вы импортируете модуль так, чтобы довольно часто? – hop

ответ

81

Вы также можете использовать struct модуль, чтобы сделать это:

>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0] 
3148270713L 
+3

Предупреждение: «L» на самом деле составляет 8 байтов (не 4) в 64-битных сборках Python, поэтому это может не получиться. –

+11

Rafał: На самом деле, поскольку Грег использовал <, в соответствии с документами L является стандартным размером (4) ", когда строка формата начинается с одной из '<', '>', '!' или '='. http://docs.python.org/library/struct.html#format-characters –

+48

Этот ответ не работает для двоичных строк произвольной длины. – amcnabb

59

Как сказал Грег, вы можете использовать-структуру, если вы имеете дело с двоичными значениями, но если у вас есть только «шестнадцатеричное число», но в байтовый формат вы можете просто преобразовать его, как:

s = 'y\xcc\xa6\xbb' 
num = int(s.encode('hex'), 16) 

... это то же самое, как:

num = struct.unpack(">L", s)[0] 

... за исключением того, что он будет работать для любого количества байтов.

+3

что такое различие между «двоичными значениями» и «шестнадцатеричным числом», но в байтовом формате »??????? – hop

+0

См. "Help struct". Например. «001122334455» .decode («hex») не может быть преобразован в число с помощью struct. –

+3

Кстати, этот ответ предполагает, что целое число закодировано в байтовом порядке большого конца. Для little-endian порядка выполните: 'int (''. Join (reverse (s)). Encode ('hex'), 16)' – amcnabb

6
import array 
integerValue = array.array("I", 'y\xcc\xa6\xbb')[0] 

Предупреждение: вышеупомянутое сильно зависит от конкретной платформы. И спецификатор «I», и сущность преобразования string-> int зависят от конкретной реализации Python. Но если вы хотите преобразовать сразу несколько целых чисел/строк, то модуль массива сделает это быстро.

206

В 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 вашего байт-строки.

Это также работает для байтов-целых чисел произвольной длины и для целых чисел со знаком с двумя символами, задавая signed=True. См. docs for from_bytes.

+0

@eri, насколько медленнее? Раньше я использовал struct, но преобразовывался в int.from_bytes, когда я пошел в py3.Я вызываю этот метод каждый мс, поскольку я получаю последовательные данные, поэтому любое ускорение приветствуется. Я смотрел на это – Naib

+0

@Naib, для 'os.urandom (4)' bytes ** 1.4 μs ** (struct) vs ** 2.3 μs ** (int.from_bytes) на моем процессоре. python 3.5.2 – eri

+2

@eri Я воскресил сценарий timeit, который я использовал для оценки нескольких CRC-методов. Четыре пробега 1) struct 2) int.from_bytes 3) как # 1, но cython скомпилирован, 4) как # 2, но cython скомпилирован. 330ns для struct, 1.14us для int (cython дал, возможно, ускорение 20ns в обоих ...) похоже, что я возвращаюсь :) это не преждевременная оптимизация, я сталкивался с некоторыми неприятными узкими местами, особенно с миллионами образцов для публикации -процесса и были отбиты части. – Naib

7

Я использую следующую функцию для преобразования данных между 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

4

В 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 для получения списка спецификаторов формата.

0

Я пытался найти решение для произвольной длины байтовых последовательностей, которые будут работать под 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) 
    
  • Данные должны быть большим обратным порядком байтов. В случае, если у вас есть немного-Endian значение, вы должны обратить его первый:

    n = signedbytes(s[::-1]) 
    

Конечно, это должно быть использовано только в том случае произвольной длины требуется. В противном случае придерживайтесь более стандартных способов (например, struct).

1

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 

гекс (bytes2int ([0x87, 0x65, 0x43, 0x21])) возвращает '0x87654321'.

Он обрабатывает большой и маленький порядок байтов и легко модифицируется для 8 байт

1
>>> 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: Append один байт, скажем 'А':

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA")) 
86738642548474510294585684247313465921L 

Тест 5: Деление на 256:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))/256 
338822822454978555838225329091068226L 

Результат равен результату теста 4, как и следовало ожидать.

0

Как указано выше, используя unpack Функция struct - это хороший способ. Если вы хотите реализовать свою собственную функцию, есть еще одно решение:

def bytes_to_int(bytes): 
    result = 0 
    for b in bytes: 
     result = result * 256 + int(b) 
return result 
Смежные вопросы