如何在Java中将字节数组转换为长整型?

7
我正在从硬件设备中读取8个字节的数据,需要将它们转换为数字值。我想将它们转换为长整型,因为这应该可以适配8个字节。我不太熟悉Java和低级数据类型操作。我似乎有两个问题(除了事实上几乎没有关于相关硬件的文档),这些字节期望是无符号的,所以我不能进行简单的整数转换。我不确定它们的字节序。

如果有任何建议,都非常感激。


最终采用了以下代码(来自我可能应该一周前就阅读的一些源代码):

public static final long toLong (byte[] byteArray, int offset, int len)
{
   long val = 0;
   len = Math.min(len, 8);
   for (int i = (len - 1); i >= 0; i--)
   {
      val <<= 8;
      val |= (byteArray [offset + i] & 0x00FF);
   }
   return val;
}

1
为什么需要将这8个字节转换为单个数字值?如果它们确实是较大值的组成字节,则它们既不是“有符号”也不是“无符号”。如果您不知道它们的字节序,那么您就不能将它们解释为某个长整型值。 - Jonathan Feinberg
我知道它们是连续的值(磁计数器),所以通过试错法,我可以找到它何时报告正确的递增。我需要将它们转换为单个数字值,以确定其变化率。 - Jotham
你应该将0x00FF替换为0xFFL,因为这样更能表达你将一个字节转换为长整型的意图。 - starblue
当你说“最终得到了这个结果”时,我觉得它似乎与 Erickson 的答案不一致,而他的答案是正确的。 - dfrankow
11个回答

16
根据数据的字节序进行位移操作相对简单。然而,对于 long 数据类型有一个小技巧,因为在整数类型的二进制操作中,int 或更小的类型会被提升为 int。对于左移大于 31 位的情况,这将导致结果为零,因为所有比特都已经移出了 int 范围之外。
因此,通过在计算中包括一个 long 操作数来强制提升为 long。下面我使用值为 0xFFL 的掩码来与每个字节进行按位与操作,从而强制结果为一个 long
byte[] buf = new byte[8];
/* Fill buf somehow... */
long l = ((buf[0] & 0xFFL) << 56) |
         ((buf[1] & 0xFFL) << 48) |
         ((buf[2] & 0xFFL) << 40) |
         ((buf[3] & 0xFFL) << 32) |
         ((buf[4] & 0xFFL) << 24) |
         ((buf[5] & 0xFFL) << 16) |
         ((buf[6] & 0xFFL) <<  8) |
         ((buf[7] & 0xFFL) <<  0) ;

也许可以使用除0以外的数组索引。 - Tom Hawtin - tackline
是的,我注意到你在评论中提到了这个问题... 我通过将掩码提升为长整型来解决了它。 - erickson
顺便提一下,0xFF等于255(0b11111111)。 - roottraveller

8

Byte#longValue()可以解决这个问题。

如果不行(感谢提供的示例代码),您可以使用java.nio.ByteBuffer,例如:

 public static long toLong(byte[] b) {
    ByteBuffer bb = ByteBuffer.allocate(b.length);
    bb.put(b);
    return bb.getLong();
}

初始顺序为BIG_ENDIAN,您可以在此处阅读更多信息


我之前有类似的东西,但还是不行。也许是因为我对Java还很陌生。public static long bufferToLong (byte[] b) { long value = b[0]; for (int i=1;i value += Math.pow(0xFF, i+1) * v.longValue(); } return value; } - Jotham
那么-1是什么意思呢?无论如何,我扩展了答案,给你另一个想法。据我所知,java.nio非常快(比普通流更快)。 - Bostone
2
除非在调用 getLong() 之前调用 bb.flip(); ,否则我会收到 BufferUnderflowException 异常。 - das_j

6
ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.getLong();

5

我相信使用java.nio会对你有所帮助。以下是如何将8个字节存储在long类型中的方法:

// Byte Array TO Long
public static long batol(byte[] buff) {
    return batol(buff, false);
}

public static long batol(byte[] buff, boolean littleEndian) {
    assert(buff.length == 8);
    ByteBuffer bb = ByteBuffer.wrap(buff);
    if (littleEndian) bb.order(ByteOrder.LITTLE_ENDIAN);
    return bb.getLong();
}

当然,得到的长整型将具有有符号表示,但它们与源数据具有相同的二进制值。如果需要64位以上的无符号表示和算术运算,则需要使用BigInteger。以下是如何将存储在长整型中的“无符号”数据转换为正确的BigInteger:

// "Unsigned" Long TO Big Integer
public static BigInteger ultobi(long ul) {
    byte[] buff = new byte[8];
    ByteBuffer.wrap(buff).asLongBuffer().put(ul);
    return new BigInteger(+1, buff);
}

2
对于字节序,可以使用已知的一些数字进行测试,然后使用字节移位将它们移动到长整型中。你可能会发现这是一个起点。参考链接:http://www.janeg.ca/scjp/oper/shift.html。难点在于根据字节序的不同,移位的方法也会有所不同,但基本上是通过移位24、16、8位来实现,然后再加上最后一个数字。如果是32位,就这样做,但是你需要更多的移位操作。

使用额外移位时,必须注意您正在移位的是 long 而不是 int,因为在 int 中移位超过 24 位将会丢弃最高位的数据。 - erickson
他说它是long类型,所以我认为他可以确定他实际需要多少位。 - James Black

1

看一下 BigInteger(byte[])。它几乎是你想要的,只是它是有符号的。所以在将其传递给 BigInteger 之前,你可以再添加一个字节。

另一件事是你应该知道你的字节是大端还是小端。

希望这可以帮到你。


0

您可以使用:

byte bVal = 127;
Long longVar = Long.valueOf(bVal);

尽管标题似乎是这样,但 OP 似乎想要将一个字节数组转换为长整型。 - LarsH

0
 public static long convertToLong(byte[] array) 
 {
   ByteBuffer buffer = ByteBuffer.wrap(array);
   return buffer.getLong();
 }

1
请不要仅仅发布代码片段,还要描述代码。 - InsaneCat

0

另一种选择

来自谷歌

com.google.common.primitives

  Longs.fromByteArray(bytes);

0

这可能是一个不错的解决方案,但是你如何处理“unsigned long”? - Maxbester

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接