将整数转换为半字节,`to_bytes()`?

Convert integer to half-byte, `to_bytes()`?

我有一个整数列表:

var = [1, 5, 4, 17, 231, 89]

如果我想将其转换为字节列表,我可以这样做:

[(i).to_bytes(1, byteorder='big') for i in var]

由于 var 中的每个值都小于 256,因此每个整数可以容纳一个字节。但如果我有另一个列表,请说:

var = [1, 2, 15, 12]

这可以将一个整数放入半个字节,或者更准确地说是我要查找的内容,每个字节放入两个整数。

如果可能的话,我如何指定将两个整数组合成一个字节,包括往返?

如下所示:

var1 = [1, 5, 4, 17, 231, 89]
var2 = [1, 2, 15, 12]

def foo(var):
  if max(var) < 2**4:
    num_bytes = 0.5
  elif max(var) < 2**8:
    num_bytes = 1
  elif max(var) < 2**16:
    num_bytes = 2

  if num_bytes >= 1:
    return [(i).to_bytes(num_bytes, byteorder='big') for i in var], num_bytes
  elif num_bytes = 0.5:
    # convert var to list of nybbles
    # combine two nybbles to a byte
    # create list of bytes (length is half length of var)
    # return it and num_bytes

def unfoo(var, num_bytes):
  if num_bytes >= 1:
    print([int.from_bytes(i, 'big') for i in var])
  elif num_bytes = 0.5:
    # print original list of integers again

我想将一个整数列表转换为一个字节列表,但如果可以的话,将两个 nybbles 放入一个字节,然后再进行转换。

期望的结果是:

a, b = foo(var1)
unfoo(a, b) # prints [1, 5, 4, 17, 231, 89]

a, b = foo(var2)
unfoo(a, b) # prints [1, 2, 15, 12]

我不想用最小位数的列表来表示单个数字。注意max(list):如果列表中的所有值都可以是8位,则适合8位;如果所有值都可以是 16 位,则适合 16 位;如果所有值都可以是 nybble,则将两个 nybble 对组成一个字节列表。

基本上,如果我有两个整数,每个整数都可以放入一个 nybble,我如何将它们连接成一个字节?如果我知道需要将字节分成两部分,我该怎么做呢?我总是可以假设原始列表可以被 2 整除。

您需要算出一个字节可以容纳多少个数字。然后,您需要将每个数字移动正确的数量并创建一个包含组合数字的新列表。假设你可以在一个字节中容纳两个数字,你会得到 new_number = (old_num1 << 4) + old_num2

def foo(var):
    num_bytes = math.ceil(1 + max(math.log2(x) for x in var)) / 8
    if num_bytes >= 1:
        num_bytes = int(num_bytes)
        return [(i).to_bytes(num_bytes, byteorder='big') for i in var], num_bytes
    elif num_bytes == 0.5:
        shift_bits = 4 # or generally, int(num_bytes * 8) 
        new_list = [(a << shift_bits) + b for a, b in zip(var[::2], var[1::2])]
        return [(i).to_bytes(1, byteorder='big') for i in new_list], num_bytes

unfoo,你需要做这个操作的逆操作:当num_bytes < 1时,我们可以在foo()函数中找到你移动的位数。按照前面的解释同名,给定new_number,我们可以得到old_num2作为最低四位(由new_number & mask找到),old_num1是最- 重要的四位(由 new_number >> shift_bits 找到)

def unfoo(var, num_bytes):
    if num_bytes >= 1:
        return [int.from_bytes(i, 'big') for i in var]
    elif num_bytes == 0.5:
        new_list = [int.from_bytes(i, 'big') for i in var]
        ret = []
        shift_bits = 4 # in general: int(num_bytes * 8)
        mask = int(2**shift_bits - 1)
        for i in new_list:
            b = i & mask
            a = (i >> shift_bits) & mask
            ret.append(a)
            ret.append(b)
        return ret

检查这是否有效:

var1 = [1, 5, 4, 17, 231, 89]
var2 = [1, 2, 15, 12]
        
a, b = foo(var1)
c = unfoo(a, b) # prints [1, 5, 4, 17, 231, 89]

print(var1)
print(c)


a2, b2 = foo(var2)
c2 = unfoo(a2, b2) # prints [1, 2, 15, 12]
print(var2)
print(c2)

给出预期的输出:

[1, 5, 4, 17, 231, 89]
[1, 5, 4, 17, 231, 89]
[1, 2, 15, 12]
[1, 2, 15, 12]

我写了两个简单的函数,一个将两个数字打包成一个字节(显然数字必须适合半个字节),另一个将它们解包:

def concatToByte(num1, num2):
    nybbleSize = 2**4
    if num1 >= nybbleSize or num2 >= nybbleSize:
        raise ValueError("Arguments must fit nybble size")
    return (num1 << 0x4 | num2).to_bytes(1, 'big')
    
def splitByte(byte):
    byteAsInt = int.from_bytes(byte, 'big')
    num1 = (byteAsInt & 0xF0) >> 0x4
    num2 = (byteAsInt & 0x0F)
    return num1, num2

那么您可以按如下方式使用它:

inNum1 = 5 # hex: 0x05
inNum2 = 12 # hex: 0x0c

byte = concatToByte(inNum1, inNum2)
print(hex(int.from_bytes(byte, 'big'))) # 0x5c

outNum1, outNum2 = splitByte(byte)
print(outNum1, outNum2) # 5 12