2009-01-14 48 views
107

Làm thế nào tôi có thể chuyển đổi một chuỗi các byte thành một int trong python?chuyển đổi một chuỗi các byte thành một int (python)

nói như thế này: 'y\xcc\xa6\xbb'

tôi đã đưa ra một/cách ngu ngốc thông minh để làm việc đó:

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

Tôi biết có phải được một cái gì đó được xây dựng trong hoặc trong thư viện chuẩn mà không hơn này đơn giản ...

Điều này khác với converting a string of hex digits mà bạn có thể sử dụng int (xxx, 16), nhưng thay vào đó tôi muốn chuyển đổi một chuỗi giá trị byte thực.

UPDATE:

tôi giống như James' câu trả lời tốt hơn một chút vì nó không đòi hỏi nhập khẩu mô-đun khác, nhưng phương pháp Greg là nhanh hơn:

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

phương pháp hacky của tôi:

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

THÔNG TIN CẬP NHẬT THÊM:

Ai đó đã hỏi ý kiến ​​những gì pr với biểu tượng nhập mô-đun khác. Vâng, nhập khẩu một mô-đun không nhất thiết phải là giá rẻ, hãy xem:

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

Bao gồm các chi phí nhập khẩu các mô-đun phủ nhận hầu hết các lợi thế mà phương pháp này có. Tôi tin rằng điều này sẽ chỉ bao gồm chi phí nhập khẩu một lần cho toàn bộ hoạt động chuẩn; hãy xem điều gì sẽ xảy ra khi tôi buộc tải lại mỗi lần:

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

Không cần phải nói, nếu bạn thực hiện rất nhiều lần thực hiện phương pháp này cho mỗi lần nhập, điều này sẽ ít tương xứng với vấn đề. Nó cũng có thể i/o chi phí hơn là CPU vì vậy nó có thể phụ thuộc vào khả năng và tải đặc điểm của máy cụ thể.

+0

và nhập nội dung nào đó từ lib chuẩn là xấu, tại sao? – hop

+0

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

+19

"cập nhật thêm" của bạn là lạ ... tại sao bạn sẽ nhập mô-đun như vậy thường xuyên? – hop

Trả lời

81

Bạn cũng có thể sử dụng các mô-đun struct để làm điều này:

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

Cảnh báo: "L" thực sự là 8 byte (không phải 4) trong bản dựng Python 64 bit, vì vậy điều này có thể không thành công ở đó. –

+11

Rafał: Không thực sự, kể từ khi Greg đang sử dụng <, theo tài liệu L là kích thước tiêu chuẩn (4) "khi chuỗi định dạng bắt đầu bằng một trong '<', '>', '!' hoặc '='. " http://docs.python.org/library/struct.html#format-characters –

+48

Câu trả lời này không hoạt động đối với các chuỗi nhị phân có độ dài tùy ý. – amcnabb

59

Như Greg nói, bạn có thể sử dụng struct nếu bạn đang đối phó với các giá trị nhị phân, nhưng nếu bạn chỉ có một "số hex" nhưng trong định dạng byte bạn có thể muốn chỉ chuyển đổi nó như:

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

... đây là giống như:

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

... ngoại trừ nó sẽ hoạt động với bất kỳ số byte nào.

+3

chính xác là sự khác biệt giữa "giá trị nhị phân" và "số hex" nhưng ở định dạng byte "??????? – hop

+0

Xem "cấu trúc trợ giúp". Ví dụ. Không thể chuyển đổi "001122334455" .decode ('hex') thành một số bằng struct. –

+3

Nhân tiện, câu trả lời này giả định rằng số nguyên được mã hóa theo thứ tự byte lớn. Đối với thứ tự nhỏ, làm: 'int (''. Join (đảo ngược (s)). Encode ('hex'), 16)' – amcnabb

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

Cảnh báo: ở trên là nền tảng cụ thể. Cả hai "I" specifier và endianness của string-> int chuyển đổi phụ thuộc vào việc thực hiện Python cụ thể của bạn. Nhưng nếu bạn muốn chuyển đổi nhiều số nguyên/chuỗi cùng một lúc, thì mô-đun mảng sẽ thực hiện nhanh chóng.

206

Trong Python 3.2 và sau đó, sử dụng

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

hoặc

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

theo endianness của byte dây của bạn.

Điều này cũng hoạt động đối với các số nguyên có độ dài tùy ý bằng nhau và đối với các số nguyên đã được bổ sung của hai số bằng cách chỉ định signed=True. Xem docs for from_bytes.

+0

@eri chậm hơn bao nhiêu? Tôi sử dụng để sử dụng struct nhưng chuyển đổi sang int.from_bytes khi tôi đã đi đến py3.Tôi đang gọi phương thức này mỗi ms khi tôi nhận dữ liệu nối tiếp nên mọi tốc độ đều được chào đón. Tôi đã xem xét điều này – Naib

+0

@Naib, cho 'os.urandom (4)' byte ** 1.4 µs ** (struct) vs ** 2.3 µs ** (int.from_bytes) trên cpu của tôi. python 3.5.2 – eri

+2

@eri Tôi hồi sinh một tập lệnh timeit i được sử dụng để đánh giá một vài phương pháp CRC. Bốn chạy 1) struct 2) int.from_bytes 3) như # 1 nhưng cython được biên dịch, 4) là # 2 nhưng đã được biên dịch. 330ns cho cấu trúc, 1,14us cho int (cython cho có thể tăng tốc 20ns trong cả hai ...) có vẻ như tôi đang chuyển đổi trở lại :) đây không phải là tối ưu hóa sớm, tôi đã gặp một số nút cổ chai khó chịu, đặc biệt với một triệu mẫu -quá trình và đã được loại bỏ các bộ phận. – Naib

7

Tôi sử dụng hàm sau để chuyển đổi dữ liệu giữa int, hex và 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') 

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

4

Trong Python 2.x, bạn có thể sử dụng các định dạng specifiers <B cho byte unsigned, và <b cho byte ký với struct.unpack/struct.pack.

ví dụ:

Hãy x = '\xff\x10\x11'

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

Và:

data_bytes = struct.pack('<' + 'B'*len(data_ints), *data_ints) # '\xff\x10\x11'
Đó * được yêu cầu!

Xem https://docs.python.org/2/library/struct.html#format-characters để biết danh sách các thông số định dạng.

0

Tôi đã cố gắng tìm một giải pháp cho chuỗi byte dài tùy ý có thể hoạt động trong Python 2.x. Cuối cùng tôi đã viết này, nó là một chút hacky bởi vì nó thực hiện một chuyển đổi chuỗi, nhưng nó hoạt động.

Function cho Python 2.x, chiều dài tùy ý

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) 

Chức năng này có hai yêu cầu:

  • Các đầu vào data cần phải được một bytearray. Bạn có thể gọi hàm như sau:

    s = 'y\xcc\xa6\xbb' 
    n = signedbytes(s) 
    
  • Dữ liệu cần phải là người lớn. Trong trường hợp bạn có một giá trị ít về cuối nhỏ, bạn nên đảo ngược nó đầu tiên:

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

Tất nhiên, điều này sẽ chỉ được sử dụng nếu chiều dài tùy ý là cần thiết. Nếu không, hãy tuân theo các cách tiêu chuẩn hơn (ví dụ: struct).

1

int.from_bytes là giải pháp tốt nhất nếu bạn đang ở phiên bản> = 3.2. Giải pháp "struct.unpack" yêu cầu một chuỗi để nó không áp dụng cho các mảng byte. Dưới đây là một giải pháp:

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])) lợi nhuận '0x87654321'.

Nó xử lý endianness lớn và nhỏ và có thể dễ dàng sửa đổi cho 8 byte

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

Test 1: nghịch đảo:

>>> hex(2043455163) 
'0x79cca6bb' 

thử nghiệm 2: Số byte> 8:

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

Kiểm tra 3: Tăng thêm một:

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

thử nghiệm 4: Nối một byte, nói 'A':

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

thử nghiệm 5: Chia cho 256:

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

quả tương đương với kết quả của thử nghiệm 4, như mong đợi.

0

Như đã đề cập ở trên sử dụng chức năng unpack của struct là một cách hay. Nếu bạn muốn triển khai chức năng của riêng mình, có một giải pháp khác:

def bytes_to_int(bytes): 
    result = 0 
    for b in bytes: 
     result = result * 256 + int(b) 
return result 
Các vấn đề liên quan