Bagaimana cara mengubah string byte menjadi int?

162

Bagaimana saya bisa mengubah string byte menjadi int di python?

Katakan seperti ini: 'y\xcc\xa6\xbb'

Saya datang dengan cara pintar / bodoh untuk melakukannya:

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

Saya tahu harus ada sesuatu bawaan atau di perpustakaan standar yang melakukan ini lebih sederhana ...

Ini berbeda dengan mengonversi string angka hex yang dapat Anda gunakan int (xxx, 16), tetapi saya ingin mengonversi string nilai byte aktual.

MEMPERBARUI:

Saya agak suka jawaban James sedikit lebih baik karena tidak perlu mengimpor modul lain, tetapi metode Greg lebih cepat:

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

Metode hacky saya:

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

PEMBARUAN LEBIH LANJUT:

Seseorang bertanya dalam komentar apa masalah dengan mengimpor modul lain. Nah, mengimpor modul belum tentu murah, lihat:

>>> Timer("""import struct\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""").timeit()
0.98822188377380371

Termasuk biaya mengimpor modul meniadakan hampir semua keuntungan yang dimiliki metode ini. Saya percaya bahwa ini hanya akan mencakup biaya impor sekali untuk seluruh proses benchmark; lihat apa yang terjadi ketika saya memaksanya memuat ulang setiap kali:

>>> Timer("""reload(struct)\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""", 'import struct').timeit()
68.474128007888794

Tak perlu dikatakan, jika Anda melakukan banyak eksekusi metode ini per satu impor daripada ini menjadi kurang proporsional masalah. Ini juga mungkin biaya i / o daripada cpu sehingga mungkin tergantung pada kapasitas dan karakteristik beban mesin tertentu.

ʞɔıu
sumber
dan mengimpor sesuatu dari lib standar itu buruk, mengapa?
26
"pembaruan lebih lanjut" Anda aneh ... mengapa Anda sering mengimpor modul ini?
5
Saya tahu ini pertanyaan lama. Tetapi jika Anda ingin menjaga perbandingan Anda dengan orang lain: Jawaban siput mekanik ( int.from_bytes) tampil struct.unpackdi komputer saya. Selanjutnya menjadi imo yang lebih mudah dibaca.
magu_

Jawaban:

110

Anda juga dapat menggunakan modul struct untuk melakukan ini:

>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L
Greg Hewgill
sumber
3
Peringatan: "L" sebenarnya 8 byte (bukan 4) dalam versi 64 bit Python build, jadi ini mungkin gagal di sana.
Rafał Dowgird
12
Rafał: Tidak juga, karena Greg menggunakan <, menurut dokumen L adalah ukuran standar (4) "ketika format string dimulai dengan salah satu dari '<', '>', '!' atau '='. " docs.python.org/library/struct.html#format-characters
André Laszlo
59
Jawaban ini tidak berfungsi untuk string biner yang panjangnya berubah-ubah.
amcnabb
4
Jenis memiliki ukuran tertentu, itu tidak akan pernah bekerja untuk string biner panjang sewenang-wenang. Anda bisa mengatur loop for untuk mengatasinya jika Anda tahu jenis setiap item.
Joshua Olson
2
"L" sebenarnya adalah uint32 (4 byte). Jika seperti dalam kasus saya Anda membutuhkan 8 byte, gunakan "Q" -> uint64. Perhatikan juga bahwa "l" -> int32 dan q -> int64
ntg
319

Dalam Python 3.2 dan yang lebih baru, gunakan

>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='big')
2043455163

atau

>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='little')
3148270713

sesuai dengan endianness byte-string Anda.

Ini juga berfungsi untuk bytestring-integer dengan panjang sewenang-wenang, dan untuk dua's melengkapi integer yang ditandatangani dengan menentukan signed=True. Lihat dokumen untukfrom_bytes .

Siput mekanik
sumber
@ aeri berapa jauh lebih lambat? Saya dulu menggunakan struct tetapi dikonversi ke int.from_bytes ketika saya pergi ke py3. Saya memanggil metode ini setiap ms karena saya menerima data serial sehingga setiap speedup diterima. Saya telah melihat ini
Naib
@Naib, untuk os.urandom(4)byte ** 1,4 µs ** (struct) vs ** 2.3 µs ** (int.from_bytes) pada cpu saya. python 3.5.2
eri
5
@ Eeri saya menghidupkan kembali skrip timeit yang saya gunakan untuk mengevaluasi beberapa metode CRC. Empat berjalan 1) struct 2) int.from_bytes 3) sebagai # 1 tetapi cython dikompilasi, 4) sebagai # 2 tetapi cython dikompilasi. 330ns untuk struct, 1.14us untuk int (cython memberi mungkin 20ns speedup di keduanya ...) sepertinya saya beralih kembali :) ini bukan optimasi prematur, saya telah memukul beberapa kemacetan yang buruk, terutama dengan sejuta sampel untuk dikirim -proses dan telah merobohkan bagian.
Naib
66

Seperti kata Greg, Anda dapat menggunakan struct jika Anda berurusan dengan nilai-nilai biner, tetapi jika Anda hanya memiliki "nomor hex" tetapi dalam format byte Anda mungkin ingin mengonversinya seperti:

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

... ini sama dengan:

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

... kecuali itu akan bekerja untuk sejumlah byte.

James Antill
sumber
3
apa sebenarnya perbedaan antara "nilai-nilai biner" dan "'hex number' tetapi dalam format byte" ???????
Lihat "bantu struct". Misalnya. "001122334455" .decode ('hex') tidak dapat dikonversi ke nomor menggunakan struct.
James Antill
3
By the way, jawaban ini mengasumsikan bahwa integer dikodekan dalam urutan byte big-endian. Untuk pesanan little-endian, lakukan:int(''.join(reversed(s)).encode('hex'), 16)
amcnabb
1
bagus tapi ini akan lambat! Tebak itu tidak terlalu penting jika Anda mengkode dengan Python.
MattCochrane
8

Saya menggunakan fungsi berikut untuk mengkonversi data antara int, hex, dan byte.

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

Sumber: http://opentechnotes.blogspot.com.au/2014/04/convert-values-to-from-integer-hex.html

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

Peringatan: di atas sangat spesifik platform. Baik specifier "I" dan endianness dari konversi string-> int tergantung pada implementasi Python Anda. Tetapi jika Anda ingin mengonversi banyak bilangan bulat / string sekaligus, maka modul array melakukannya dengan cepat.

Rafał Dowgird
sumber
5

Di Python 2.x, Anda bisa menggunakan penentu format <Buntuk byte yang tidak ditandatangani, dan <buntuk byte yang ditandatangani dengan struct.unpack/ struct.pack.

Misalnya:

Biarkan x='\xff\x10\x11'

data_ints = struct.unpack('<' + 'B'*len(x), x) # [255, 16, 17]

Dan:

data_bytes = struct.pack('<' + 'B'*len(data_ints), *data_ints) # '\xff\x10\x11'

Itu *wajib!

Lihat https://docs.python.org/2/library/struct.html#format-characters untuk daftar penentu format.

Tetralux
sumber
3
>>> reduce(lambda s, x: s*256 + x, bytearray("y\xcc\xa6\xbb"))
2043455163

Tes 1: terbalik:

>>> hex(2043455163)
'0x79cca6bb'

Tes 2: Jumlah byte> 8:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAAA"))
338822822454978555838225329091068225L

Uji 3: Bertambah satu:

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

Tes 4: Tambahkan satu byte, katakan 'A':

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

Tes 5: Bagilah dengan 256:

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

Hasil sama dengan hasil Tes 4, seperti yang diharapkan.

pengguna3076105
sumber
1

Saya berjuang untuk menemukan solusi untuk urutan byte panjang sewenang-wenang yang akan bekerja di bawah Python 2.x. Akhirnya saya menulis yang ini, sedikit hacky karena melakukan konversi string, tetapi berhasil.

Fungsi untuk Python 2.x, panjang sewenang-wenang

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)

Fungsi ini memiliki dua persyaratan:

  • Input dataharus berupa a bytearray. Anda dapat memanggil fungsi seperti ini:

    s = 'y\xcc\xa6\xbb'
    n = signedbytes(s)
  • Data harus big-endian. Jika Anda memiliki nilai little-endian, Anda harus membalikkannya terlebih dahulu:

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

Tentu saja, ini harus digunakan hanya jika panjang sewenang-wenang diperlukan. Kalau tidak, tetap dengan cara yang lebih standar (misalnya struct).

Andrea Lazzarotto
sumber
1

int.from_bytes adalah solusi terbaik jika Anda berada di versi> = 3.2. Solusi "struct.unpack" memerlukan string sehingga tidak akan berlaku untuk array byte. Ini solusi lain:

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])) mengembalikan '0x87654321'.

Ini menangani endianness besar dan kecil dan mudah dimodifikasi untuk 8 byte

pengguna3435121
sumber
1

Seperti disebutkan di atas menggunakan unpackfungsi struct adalah cara yang baik. Jika Anda ingin mengimplementasikan fungsi Anda sendiri ada solusi lain:

def bytes_to_int(bytes):
    result = 0
    for b in bytes:
        result = result * 256 + int(b)
return result
abdullahselek
sumber
Ini tidak berfungsi untuk angka negatif yang dikonversi ke byte.
Maria
1

Dalam python 3 Anda dapat dengan mudah mengkonversi string byte menjadi daftar integer (0..255) oleh

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

Metode yang sopan cepat menggunakan array.array saya telah menggunakan untuk beberapa waktu:

variabel yang telah ditentukan:

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]

ke int: (baca)

val = 0
for v in arr[offset:offset+size][::pow(-1,not big)]: val = (val<<8)|v

dari int: (tulis)

val = 16384
arr[offset:offset+size] = \
    array('B',((val>>(i<<3))&255 for i in range(size)))[::pow(-1,not big)]

Mungkin ini bisa lebih cepat.

EDIT:
Untuk beberapa angka, inilah tes kinerja (Anaconda 2.3.0) yang menunjukkan rata-rata stabil saat dibaca dibandingkan dengan 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%

Ini adalah tes kinerja mentah, sehingga endian pow-flip ditinggalkan.
The shiftfungsi yang ditampilkan berlaku operasi pergeseran-oring sama dengan untuk loop, dan arrhanya array.array('B',[0,0,255,0])karena memiliki performa tercepat berulang sebelah dict.

Saya mungkin juga harus mencatat efisiensi diukur dengan akurasi dengan waktu rata-rata.

Tcll
sumber