Как преобразовать строку байтов в int в Python - PullRequest
142 голосов
/ 14 января 2009

Как я могу преобразовать строку байтов в 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

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

Ответы [ 11 ]

0 голосов
/ 03 июня 2019

Довольно быстрый метод с использованием 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.

Должен также отметить, что эффективность измеряется с точностью до среднего времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...