Làm thế nào để chuyển đổi một chuỗi byte thành một int?


162

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

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

Tôi đã nghĩ ra một cách thông minh / ngu ngốc để làm điều đó:

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

Tôi biết rằng phải có một cái gì đó dựng sẵn hoặc trong thư viện tiêu chuẩn thực hiện việc này đơn giản hơn ...

Điều này khác với việc chuyển đổi một chuỗi các chữ số hex 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 các giá trị byte thực tế.

CẬP NHẬT:

Tôi giống như câu trả lời của James tốt hơn một chút vì nó không yêu cầu nhập mô-đun khác, nhưng phương pháp của Greg 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 hack của tôi:

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

CẬP NHẬT THÊM:

Có người hỏi ý kiến ​​về vấn đề nhập mô-đun khác. Chà, nhập một mô-đun không nhất thiết phải rẻ, hãy xem:

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

Bao gồm chi phí nhập khẩu mô-đun phủ nhận gần như tất cả 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 nó một lần cho toàn bộ hoạt động chuẩn; hãy nhìn những gì xảy ra khi tôi buộc nó 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 đang thực hiện rất nhiều lần thực hiện phương thức này cho mỗi lần nhập thì điều này sẽ trở thành một vấn đề tương đối ít hơn. Nó cũng có thể là chi phí thay vì cpu vì vậy nó có thể phụ thuộc vào công suất và đặc tính tải của máy cụ thể.


và nhập một cái gì đó từ lib tiêu chuẩn là xấu, tại sao?


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

5
Tôi biết đây là câu hỏi cũ. Nhưng nếu bạn muốn cập nhật so sánh của mình cho người khác: Câu trả lời của ốc cơ học ( int.from_bytes) được thực hiện struct.unpacktrên máy tính của tôi. Bên cạnh là imo dễ đọc hơn.
magu_

Câu trả lời:


110

Bạn cũng có thể sử dụng 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 các bản dựng Python 64 bit, vì vậy điều này có thể thất bại ở đó.
Rafał Dowgird

12
Rafał: Không thực sự, vì Greg đã 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 '='. " docs.python.org/l Library /struct.html
André Laszlo

59
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

4
Các loại có kích thước cụ thể, nó sẽ không bao giờ hoạt động đối với các chuỗi nhị phân có độ dài tùy ý. Bạn có thể thiết lập một vòng lặp for để xử lý nếu bạn biết loại của từng mục.
Joshua Olson

2
"L" thực sự là uint32 (4 byte). Nếu như trong trường hợp của tôi, bạn cần 8 byte, hãy sử dụng "Q" -> uint64. Cũng lưu ý rằng "l" -> int32 và q -> int64
ntg

319

Trong Python 3.2 trở lên, hãy sử dụng

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

hoặc là

>>> 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 bytestring có độ dài tùy ý và cho hai số nguyên được ký bổ sung bằng cách chỉ định signed=True. Xem tài liệu chofrom_bytes .


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

@Naib, đối với os.urandom(4)byte ** 1.4 Tập tin ** (struct) so với ** 2.3 Tập tin ** (int.from_bytes) trên cpu của tôi. trăn 3.5.2
eri

5
@eri Tôi đã phục hồi một tập lệnh thời gian tôi đã sử dụng để đánh giá một vài phương thức CRC. Bốn chạy 1) struct 2) int.from_bytes 3) là # 1 nhưng cython được biên dịch, 4) là # 2 nhưng cython được biên dịch. 330ns cho struct, 1.14us cho int (cython có thể tăng tốc 20ns trong cả hai ...) có vẻ như tôi đang quay trở lại :) đây không phải là tối ưu hóa sớm, tôi đã gặp phải một số tắc nghẽn khó chịu, đặc biệt là với một triệu mẫu để đăng -Quá trình và đã được gõ các bộ phận.
Naib

66

Như Greg đã nói, bạn có thể sử dụng struct nếu bạn đang xử lý các giá trị nhị phân, nhưng nếu bạn chỉ có "số hex" nhưng ở định dạng byte, bạn có thể muốn chuyển đổi nó như sau:

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

... điều này 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 sự khác biệt giữa "giá trị nhị phân" và "số hex" nhưng ở định dạng byte "???????

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

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 cuối lớn. Đối với đơn hàng nhỏ, hãy thực hiện:int(''.join(reversed(s)).encode('hex'), 16)
amcnabb

1
tốt nhưng điều này sẽ chậm! Đoán rằng điều đó không thực sự quan trọng nếu bạn đang viết mã bằng Python.
MattCochrane

8

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


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ể mạnh mẽ. Cả bộ xác định "I" và độ bền của chuyển đổi chuỗi-> int đều phụ thuộc vào việc triển khai 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.


5

Trong Python 2.x, bạn có thể sử dụng các chỉ định định dạng <Bcho các byte không dấu và <bcho các 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'

Đó *là điều bắt buộc!

Xem https://docs.python.org/2/l Library /struct.html # format- char character cho danh sách các chỉ định định dạng.


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

Kiểm tra 1: nghịch đảo:

>>> hex(2043455163)
'0x79cca6bb'

Kiểm tra 2: Số byte> 8:

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

Bài kiểm tra 3: Tăng thêm một:

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

Kiểm tra 4: Nối một byte, nói 'A':

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

Kiểm tra 5: Chia cho 256:

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

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


1

Tôi đã vật lộn để tìm một giải pháp cho các chuỗi byte có độ dài tùy ý sẽ hoạt động theo Python 2.x. Cuối cùng tôi đã viết cái này, nó hơi hack vì nó thực hiện chuyển đổi chuỗi, nhưng nó hoạt động.

Hàm cho Python 2.x, độ 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)

Hàm này có hai yêu cầu:

  • Đầu vào datacần phải là a bytearray. Bạn có thể gọi hàm như thế này:

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

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

Tất nhiên, điều này chỉ nên được sử dụng nếu cần độ dài tùy ý. Nếu không, hãy gắn bó với những 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 ở phiên bản> = 3.2. Giải pháp "struct.unpack" yêu cầu một chuỗi nên nó sẽ không áp dụng cho các mảng byte. Đây là một giải pháp khác:

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 (byte2int ([0x87, 0x65, 0x43, 0x21])) trả về '0x87654321'.

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


1

Như đã đề cập ở trên sử dụng unpackchức năng của struct là một cách tốt. Nếu bạn muốn thực hiện 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

Điều này không hoạt động đối với số âm được chuyển đổi thành byte.
Maria

1

Trong python 3, bạn có thể dễ dàng chuyển đổi một chuỗi byte thành một danh sách các số nguyên (0..255) bằng cách

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

0

Một phương thức nhanh chóng sử dụng mảng.array tôi đã sử dụng một thời gian:

các biến được xác định trước:

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]

đến int: (đọc)

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

từ int: (viết)

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

Có thể những thứ này có thể nhanh hơn.

EDIT:
Đối với một số số, đây là bài kiểm tra hiệu suất (Anaconda 2.3.0) cho thấy mức trung bình ổn định khi đọc so với 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%

Đây là một bài kiểm tra hiệu năng thô, vì vậy pow-flip endian bị bỏ lại.
Các shiftchức năng thể hiện áp dụng các hoạt động shift-oring giống như vòng lặp for, và arrchỉ là array.array('B',[0,0,255,0])vì nó có hiệu quả hoạt động lặp đi lặp lại nhanh nhất bên cạnh dict.

Tôi có lẽ cũng nên lưu ý hiệu quả được đo bằng độ chính xác đến thời gian trung bình.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.