Як перетворити рядок байтів у int?


162

Як я можу перетворити рядок байтів у int у python?

Скажіть так: 'y\xcc\xa6\xbb'

Я придумав розумний / дурний спосіб зробити це:

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

Я знаю, що має бути щось вбудоване або в стандартній бібліотеці, що робить це простіше ...

Це відрізняється від перетворення рядка шістнадцяткових цифр, для яких можна використовувати int (xxx, 16), але натомість я хочу перетворити рядок фактичних байтових значень.

ОНОВЛЕННЯ:

Мені подобається відповідь Джеймса трохи краще, тому що для цього не потрібно імпортувати інший модуль, але метод Грега швидший:

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

Потрібно сказати, що якщо ви робите багато виконання цього методу за один імпорт, це стає пропорційно меншим питанням. Це також, ймовірно, вартість вводу / виводу, а не процесор, тому це може залежати від характеристик потужності та навантаження конкретної машини.


і імпортувати щось із стандартної лібери погано, чому?

andyway, дублюють: stackoverflow.com/questions/5415 / ...

26
ваше "подальше оновлення" дивне ... чому б ви імпортували модуль так часто?

5
Я знаю, це старе питання. Але якщо ви хочете продовжувати своє порівняння актуальним для інших людей: Механічна відповідь равлика ( int.from_bytes) виконана struct.unpackна моєму комп’ютері. Поруч з тим, що є більш читабельним imo.
magu_

Відповіді:


110

Ви також можете скористатися модулем Stru для цього:

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

3
Попередження: "L" - це насправді 8 байт (а не 4) у 64-бітових побудовах Python, тому це може бути невдалим.
Rafał Dowgird

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

59
Ця відповідь не працює для двійкових рядків довільної довжини.
amcnabb

4
Типи мають певні розміри, вони ніколи не працюватимуть для двійкових рядків довільної довжини. Ви можете встановити цикл для обробки, якщо ви знаєте тип кожного елемента.
Джошуа Олсон

2
"L" - це фактично uint32 (4 байти). Якщо як у моєму випадку вам потрібно 8 байт, використовуйте "Q" -> uint64. Також зауважте, що "l" -> int32 і q -> int64
ntg

319

У 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

відповідно до витривалості вашого байтового рядка.

Це також працює для бістрових цілих чисел довільної довжини, а для двох підписаних цілих чисел шляхом вказівки signed=True. Дивіться документи дляfrom_bytes .


@eri наскільки повільніше? Коли я перейшов до py3, я використовував структуру, але перетворювався на int.from_bytes. Я закликаю цей метод кожні мс, коли отримую послідовні дані, тому будь-яке прискорення вітається. Я дивився на це
Найб

@Naib, для os.urandom(4)байтів ** 1,4 мкс ** (структура) проти ** 2,3 мкс ** (int.from_bytes) на моєму процесорі. python 3.5.2
eri

5
@eri Я воскресив сценарій timeit, який я використав для оцінки кількох методів CRC. Чотири запуски 1) Структура 2) int.from_bytes 3) як №1, але створений цитон, 4) як №2, але створений цитон. 330ns для struct, 1,14us для int (cython дав, можливо, 20ns прискорення в обох ...) виглядає так, що я перемикаюсь назад :) Це не передчасна оптимізація, я стикався з деякими неприємними вузькими місцями, особливо з мільйоном зразків для публікації -обробляти і збивати деталі.
Найб

66

Як сказав Грег, ви можете використовувати структура, якщо ви маєте справу з бінарними значеннями, але якщо у вас просто "шістнадцятковий номер", але у байтовому форматі, ви можете просто перетворити його так:

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

... це те саме, що:

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

... крім цього він буде працювати для будь-якої кількості байтів.


3
у чому саме різниця між "двійковими значеннями" та "" шістнадцятковим числом ", але у байтовому форматі" ???????

Див. "Структура довідки". Напр. "001122334455" .decode ('hex') не може бути перетворений у число за допомогою struct.
Джеймс Антілл

3
До речі, ця відповідь передбачає, що ціле число кодується в порядку байтів великого рівня. Для замовлення мало- int(''.join(reversed(s)).encode('hex'), 16)
ендіанських дій

1
добре, але це буде повільно! Здогадайтесь, що насправді не має значення, якщо ви кодуєте в Python.
MattCochrane

8

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


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

Попередження: вищезазначене сильно залежить від платформи. І специфікатор "Я", і витривалість перетворення string-> int залежать від вашої конкретної реалізації Python. Але якщо ви хочете перетворити багато цілих чисел / рядків одночасно, то модуль масиву робить це швидко.


5

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


3
>>> 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, як і очікувалося.


1

Я намагався знайти рішення для довільних байтових послідовностей, які працювали б під 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повинен бути a bytearray. Ви можете викликати функцію так:

    s = 'y\xcc\xa6\xbb'
    n = signedbytes(s)
  • Дані повинні бути великими. Якщо у вас є малоцінне значення, спершу слід змінити його:

    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

hex (bytes2int ([0x87, 0x65, 0x43, 0x21])) повертає "0x87654321".

Він обробляє велику і маленьку витривалість і легко змінюється на 8 байт


1

Як було сказано вище, використання unpackфункції struct є хорошим способом. Якщо ви хочете реалізувати власну функцію, є ще одне рішення:

def bytes_to_int(bytes):
    result = 0
    for b in bytes:
        result = result * 256 + int(b)
return result

Це не працює для негативного числа, яке було перетворено в байти.
Марія

1

У python 3 ви можете легко перетворити рядок байтів у список цілих чисел (0..255)

>>> list(b'y\xcc\xa6\xbb')
[121, 204, 166, 187]

0

Пристойно швидкий метод, що використовує 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%

Це необмежений тест на працездатність, тому ендіанський потік не залишається.
Показана shiftфункція застосовує ту ж саму операцію зсуву, як і для циклу, і arrсаме так, array.array('B',[0,0,255,0])як вона має найшвидший ітераційний показник поруч dict.

Я, мабуть, також слід зазначити, що ефективність вимірюється точністю до середнього часу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.