วิธีแปลงสตริงไบต์เป็น int?


162

ฉันจะแปลงสตริงไบต์เป็น int ในไพ ธ อนได้อย่างไร

พูดอย่างนี้: 'y\xcc\xa6\xbb'

ฉันคิดวิธีที่ฉลาด / โง่ขึ้นมา:

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

ฉันรู้ว่าต้องมีบางสิ่งในตัวหรือในห้องสมุดมาตรฐานที่ทำสิ่งนี้ได้ง่ายกว่า ...

ซึ่งแตกต่างจากการแปลงสตริงของตัวเลขฐานสิบหกซึ่งคุณสามารถใช้ int (xxx, 16) ได้ แต่ฉันต้องการแปลงสตริงของค่าไบต์จริงแทน

UPDATE:

ฉันชอบคำตอบของ James ที่ดีขึ้นเล็กน้อยเพราะไม่ต้องการนำเข้าโมดูลอื่น แต่วิธีของ Greg เร็วขึ้น:

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

ไม่จำเป็นต้องพูดว่าหากคุณทำการประมวลผลจำนวนมากของวิธีนี้ต่อหนึ่งการนำเข้าซึ่งจะทำให้เกิดปัญหาน้อยลงตามสัดส่วน อาจเป็นค่าใช้จ่ายของ i / o มากกว่า cpu ดังนั้นจึงอาจขึ้นอยู่กับความจุและลักษณะของโหลดของเครื่องนั้น ๆ


และการนำเข้าบางสิ่งจาก lib มาตรฐานไม่ดีทำไม

andyway, ซ้ำกัน: stackoverflow.com/questions/5415/…

26
"การอัพเดทเพิ่มเติม" ของคุณแปลกประหลาด ... ทำไมคุณต้องนำเข้าโมดูลบ่อยครั้ง

5
ฉันรู้ว่านี่เป็นคำถามเก่า แต่ถ้าคุณต้องการให้คนอื่นเปรียบเทียบวันที่มากที่สุด: คำตอบของหอยทากเครื่องกล ( int.from_bytes) ทำออกมาstruct.unpackในคอมพิวเตอร์ของฉัน ถัดจากการเป็น IMO ที่อ่านง่ายขึ้น
magu_

คำตอบ:


110

คุณยังสามารถใช้โมดูลstructเพื่อทำสิ่งนี้:

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

3
คำเตือน: "L" เป็นจริง 8 ไบต์ (ไม่ใช่ 4) ใน Python 64 บิตที่สร้างดังนั้นสิ่งนี้อาจล้มเหลว
Rafał Dowgird

12
Rafał: ไม่ใช่เพราะ Greg ใช้ <ตามเอกสาร L คือขนาดมาตรฐาน (4) "เมื่อสตริงรูปแบบเริ่มต้นด้วยหนึ่งใน '<', '>', '!' หรือ '='. " docs.python.org/library/struct.html#format-characters
André Laszlo

59
คำตอบนี้ใช้ไม่ได้กับสตริงไบนารีที่มีความยาวตามอำเภอใจ
amcnabb

4
ประเภทมีขนาดเฉพาะมันจะไม่ทำงานกับสตริงไบนารีที่มีความยาวตามอำเภอใจ คุณสามารถตั้งค่า a สำหรับลูปเพื่อจัดการกับมันได้หากคุณรู้ประเภทของแต่ละรายการ
Joshua Olson

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

ตามendiannessของไบต์สตริงของคุณ

นอกจากนี้ยังทำงานสำหรับ bytestring-จำนวนเต็มของความยาวโดยพลการและสำหรับ signed=Truetwo's-สมบูรณ์ลงนามจำนวนเต็มโดยการระบุ ดูเอกสารสำหรับfrom_bytes


@eri ช้ากว่ามากแค่ไหน? ฉันเคยใช้ struct แต่แปลงเป็น int.from_bytes เมื่อฉันไปที่ py3 ฉันเรียกวิธีนี้ว่า ms ทุกครั้งที่ฉันได้รับข้อมูลอนุกรมดังนั้นการเร่งความเร็วก็ยินดีต้อนรับ ฉันได้ดูสิ่งนี้แล้ว
Naib

@Naib สำหรับos.urandom(4)ไบต์ ** 1.4 ** s ** (struct) vs ** 2.3 µs ** (int.from_bytes) บนซีพียูของฉัน python 3.5.2
eri

5
@eri ฉันฟื้นสคริปต์ timeit ที่ฉันใช้ใหม่เพื่อประเมินเมธอด CRC สองสามข้อ สี่วิ่ง 1) struct 2) int.from_bytes 3) เป็น # 1 แต่รวบรวม cython, 4) เป็น # 2 แต่รวบรวม cython 330ns สำหรับ struct, 1.14us สำหรับ int (cython ให้ความเร็ว 20ns ในทั้งสองอย่าง ... ) ดูเหมือนว่าฉันกำลังสลับกลับ :) นี่ไม่ใช่การเพิ่มประสิทธิภาพก่อนวัยอันควรฉันได้ตีคอขวดที่น่ารังเกียจโดยเฉพาะอย่างยิ่งกับตัวอย่างกว่าล้านรายการ - ประมวลผลและมีการเคาะชิ้นส่วนออก
Naib

66

ตามที่ Greg กล่าวไว้คุณสามารถใช้ struct หากคุณกำลังจัดการกับค่าไบนารี แต่ถ้าคุณมี "เลขฐานสิบหก" แต่ในรูปแบบไบต์คุณอาจต้องการแปลงเป็น:

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

... นี่เป็นเช่นเดียวกับ:

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

... ยกเว้นว่าจะใช้ได้กับจำนวนไบต์ใด ๆ


3
อะไรคือความแตกต่างระหว่าง "ค่าไบนารี" และ "เลขฐานสิบหก" แต่ในรูปแบบไบต์ "????????

ดู "ความช่วยเหลือของ struct" เช่น. "001122334455" .decode ('hex') ไม่สามารถแปลงเป็นตัวเลขโดยใช้ struct ได้
James Antill

3
โดยวิธีการคำตอบนี้สันนิษฐานว่าจำนวนเต็มถูกเข้ารหัสในลำดับไบต์ big-endian สำหรับคำสั่งเล็ก ๆ น้อย ๆ ให้ทำ:int(''.join(reversed(s)).encode('hex'), 16)
amcnabb

1
ดี แต่นี่จะช้า! เดาว่าไม่สำคัญถ้าคุณกำลังเขียนโค้ดใน Python
MattCochrane

8

ฉันใช้ฟังก์ชันต่อไปนี้เพื่อแปลงข้อมูลระหว่าง int, hex และ bytes

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]

คำเตือน: ด้านบนเป็นแพลตฟอร์มเฉพาะอย่างยิ่ง ทั้งตัวระบุ "ฉัน" และ endianness ของการแปลงสตริง -> 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 bytearrayคุณอาจเรียกฟังก์ชั่นเช่นนี้:

    s = 'y\xcc\xa6\xbb'
    n = signedbytes(s)
  • ข้อมูลจะต้องมีขนาดใหญ่ - endian ในกรณีที่คุณมีค่าเล็กน้อย endian คุณควรย้อนกลับก่อน:

    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

ฐานสิบหก (bytes2int ([0x87, 0x65, 0x43, 0x21])) ส่งคืน '0x87654321'

มันจัดการ endianness ใหญ่และน้อยและสามารถแก้ไขได้อย่างง่ายดายสำหรับ 8 ไบต์


1

ดังกล่าวข้างต้นโดยใช้unpackฟังก์ชั่นของstructเป็นวิธีที่ดี หากคุณต้องการใช้ฟังก์ชั่นของคุณเองมีวิธีแก้ไขปัญหาอื่น:

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

วิธีนี้ใช้ไม่ได้กับจำนวนลบที่ถูกแปลงเป็นไบต์
Maria

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%

นี่คือการทดสอบประสิทธิภาพแบบดิบดังนั้น endian pow-flip จึงถูกปล่อยออกมา ฟังก์ชั่นแสดงนำไปใช้การดำเนินการกะ oring เดียวกันเป็นห่วงและเป็นเพียงที่มีประสิทธิภาพการทำงานที่เร็วที่สุดซ้ำติดกับ
shiftarrarray.array('B',[0,0,255,0])dict

ฉันควรทราบด้วยว่าประสิทธิภาพวัดได้จากความแม่นยำถึงเวลาเฉลี่ย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.