使用 ByteBuffer 将 n 字节数组转换为整数

Posted

技术标签:

【中文标题】使用 ByteBuffer 将 n 字节数组转换为整数【英文标题】:Converting a n-bytes array to integer with ByteBuffer 【发布时间】:2020-07-12 13:23:20 【问题描述】:

我需要将任意大小的字节数组转换为short/int/long。 这意味着我可以接收到3 字节以转换为int

final byte[] bytes =  0b0000, 0b0000, 0b1111 ; // 15 - big endian
final int value = doConversion(bytes);

因此,我正在尝试提出一个通用函数。

ByteBuffer 转换在数组的大小正好代表 shortintlong 值时非常有效。但是,如果我将 int 表示为单个字节怎么办?

final byte[] bytes =  0b1111 ; // 15

似乎使用ByteBuffer 将这样的字节数组转换为int 需要调整数组的大小并对其进行填充。 负值会使事情变得更加复杂,因为填充需要使用最高有效位

final byte[] bytes =  (byte) 0b11110001 ; // -15 stored as two's complement

有没有更简单的方法来完成这项任务,还是我应该只使用自定义代码? 例如,在 Kotlin 中,使用扩展函数:

fun ByteArray.toShort(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Short =
    toInteger(Short.SIZE_BYTES, byteOrder, signed).toShort()

fun ByteArray.toInt(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Int =
    toInteger(Int.SIZE_BYTES, byteOrder, signed).toInt()

fun ByteArray.toLong(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Long =
    toInteger(Long.SIZE_BYTES, byteOrder, signed)

private fun ByteArray.toInteger(
     typeBytes: Int /* 2, 4, 8 */,
     byteOrder: ByteOrder /* little, big */, 
     signed: Boolean,
): Long 
    // Checks omitted...

    // If the byte array is bigger than the type bytes, it needs to be truncated
    val bytes =
        if (size > typeBytes) 
            if (byteOrder == LITTLE) 
                copyOf(typeBytes)
             else 
                copyOfRange(size - typeBytes, size)
            
         else 
            copyOf()
        

    val negative =
        signed && (this[if (byteOrder == LITTLE) bytes.size - 1 else 0]).toInt() and 0b1000000 != 0

    if (!negative) 
        return bytes.absoluteToLong(byteOrder)
    

    // The number is stored as two's complement.
    // Thus we invert each byte and then sum 1 to obtain the absolute value
    for (i in bytes.indices) 
        bytes[i] = bytes[i].inv()
    

    return -(bytes.absoluteToLong(byteOrder) + 1)


private fun ByteArray.absoluteToLong(byteOrder: ByteOrder): Long 
    var result = 0L
    var shift = 8 * (size - 1)
    val range =
        if (byteOrder == LITTLE) 
            size - 1 downTo 0
         else 
            0 until size
        

    for (i in range) 
        result = (this[i].toInt() and 0b11111111).toLong() shl shift or result
        shift -= 8
    

    return result

【问题讨论】:

【参考方案1】:

BigInteger 类有一个方便此用例的构造函数。示例:

byte[] bytes =  0b1111 ; 
int value = new BigInteger(bytes).intValue(); 

它为你做“符号扩展”:如果数组中的第一个字节值为负,则最终结果为负。

它还具有将值检索为其他数字类型(短、长、...)的方法。

【讨论】:

谢谢!我认为接受的字节需要始终采用大端序。另外(也许题外话),无符号值呢?例如255 表示为单个字节。 注意到了更新。也可能添加如何考虑有符号/无符号值。例如byte[] bytes = (byte) 0b11111111 // 255 as a single byte 将使用 new BigInteger(bytes).intValue() & 0xFF 进行转换。 正确,它采用大端序。至于无符号,我还没有测试过,但是添加一个零字节作为填充应该可以工作。 但是应用两个的补码只会改变符号。例如,new BigInteger(new byte[] (byte) 0b11111111 /* 255 */).intValue() 将返回 -1,应用二进制补码将简单地使其变为 1。也许我误解了你的意思 哦,好的,我看到你被编辑了。没问题!

以上是关于使用 ByteBuffer 将 n 字节数组转换为整数的主要内容,如果未能解决你的问题,请参考以下文章

如何从 ByteBuffer 转换为 Integer 和 String?

将Java位图转换为字节数组

将整数转换为字节数组(Java)

如何将 ByteBuffer 转换为 pdf

ByteBuffer 到字节数组

Java bytebuffer 将三个字节转换为 int