簡體   English   中英

如何將字節字符串轉換為int?

[英]How to convert a string of bytes into an int?

如何在python中將字節字符串轉換為int?

像這樣說: 'y\\xcc\\xa6\\xbb'

我想出了一個聰明/愚蠢的方法:

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

我知道必須有一些內置的東西或在標准庫中可以更簡單地做到這一點......

這與轉換可以使用 int(xxx, 16) 的十六進制數字字符串不同,但我想轉換實際字節值的字符串。

更新:

我有點喜歡 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,因此它可能取決於特定機器的容量和負載特性。

在 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

根據字節串​​的字節序

這也適用於任意長度的字節串整數,以及通過指定signed=True二進制補碼有符號整數。 請參閱from_bytes文檔

您還可以使用struct模塊來執行此操作:

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

正如格雷格所說,如果您正在處理二進制值,您可以使用 struct ,但如果您只有一個“十六進制數”但采用字節格式,您可能只想將其轉換為:

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

...這與:

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

...除了它適用於任意數量的字節。

我使用以下函數在整數、十六進制和字節之間轉換數據。

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

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

警告:以上內容是強平台特定的。 “I”說明符和 string->int 轉換的字節序都取決於您的特定 Python 實現。 但是如果你想一次轉換許多整數/字符串,那么數組模塊會很快完成。

在 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獲取格式說明符列表。

>>> 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 的結果。

我正在努力尋找可以在 Python 2.x 下工作的任意長度字節序列的解決方案。 最后我寫了這個,它有點hacky,因為它執行字符串轉換,但它有效。

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)
  • 數據需要是大端的。 如果你有一個 little-endian 值,你應該先反轉它:

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

當然,這應該僅在需要任意長度時使用。 否則,堅持使用更標准的方式(例如struct )。

如果你的版本 >=3.2,int.from_bytes 是最好的解決方案。 “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

hex( bytes2int( [0x87, 0x65, 0x43, 0x21])) 返回'0x87654321'。

它處理大字節序和小字節序,並且很容易修改為 8 個字節

如上所述,使用struct 的unpack函數是一個好方法。 如果您想實現自己的功能,還有另一種解決方案:

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

使用 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])因為它的迭代性能array.array('B',[0,0,255,0]) dict最快。

我可能還應該注意到效率是通過平均時間的准確性來衡量的。

在python 3中,您可以通過以下方式輕松地將字節字符串轉換為整數列表(0..255)

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM