开发者

How do I convert a byte array to a long in Java?

开发者 https://www.devze.com 2022-12-08 16:30 出处:网络
I am reading 8 bytes of data in from a hardware device.I need to convert them into a numeric value.I think I want to convert them to a long as that should fit 8 bytes.I am not very familiar with Java

I am reading 8 bytes of data in from a hardware device. I need to convert them into a numeric value. I think I want to convert them to a long as that should fit 8 bytes. I am not very familiar with Java and low level data type operations. I seem to have two problems (apart from the fact there is almost no documentation for the hardware in question), The bytes are expecting to be unsigned, so I can't do a straight 开发者_如何学运维integer conversion. I am not sure what endianness they are.

Any advice would be appreciated.


Ended up with this (taken from some source code I probably should have read a week ago):

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


Shifting bytes according to the endianness of the data is fairly straightforward. There is a small trick with the long datatype, however, because a binary operation on integral types of int or smaller promotes the operands to int. For left shifts larger than 31 bits, this will result in zero, since all of the bits have been shifted out of the int range.

So, force promotion to long by including a long operand in the calculation. Below, I do this by masking each byte with the value 0xFFL, which is a long, forcing the result to be a 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) ;


Byte#longValue() should do it

And if not (thanks for the source example) you can use java.nio.ByteBuffer such as in

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

The initial order is BIG_ENDIAN you can reed more here


I believe that you could benefit from using java.nio. This is how you can store 8 bytes in a 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();
}

Of course, the resulting longs will have signed representation, but they will have identical binary values to the source data. For a 64 bit+ unsigned representation and arithmatic, you'll need to use BigInteger. Here's how to convert from "unsigned" data stored in a long to a correct 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);
}


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


For the endianness, test with some numbers you know, and then you will be using a byte shifting to move them into the long.

You may find this to be a starting point. http://www.janeg.ca/scjp/oper/shift.html

The difficulty is that depending on the endianess will change how you do it, but you will shift by 24, 16, 8 then add the last one, basically, if doing 32 bits, but you are going longer, so just do extra shifting.


Take a look at BigInteger(byte[]). It is almost what you want except that it is a signed one. So you may add one more byte to it before you pass it on to BigInteger.

Another thing is that you should be aware of what endian your bytes are.

Hope this helps.


If you're reading from an InputStream, you may also want to look at DataInputStream.readLong(). Java 1.5 introduced Long.reverseBytes(long) which may help you with endianness.


You can use:

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


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


The next code parses bytes as a signed number of arbitrary length ≤ 8.

static long bytesToSignedNumber(boolean reverseOrder, byte... bytes) {
    if (bytes.length > 8) bytes = Arrays.copyOfRange(bytes, 0, 8); //delete this line to ignore the higher excess bytes instead of the lower ones

    int l = bytes.length;
    long number = 0;
    for (int i = 0; i < l; i++) {
        long current;
        if (l==3 || l==5 || l==6 || l==7 //delete this line if you want to operate with 3,5,6,7-byte signed numbers (unlikely)
                || !reverseOrder && (i > 0)
                || reverseOrder && (i < l-1)) {
            current = ((long) bytes[i]) & 0x0000_0000_0000_00ff; //unsigned
        } else {
            current = (long) bytes[i];                           //signed
        }
        if (reverseOrder) number += current << (8 * i);
        else number = (number << 8) + current;
    }

    return number;
}

It parses an byte array as a number, of minimum existing type, converted to long. A few examples:

bytesToSignedNumber(false, 0x01, 0x04) returns 260 (2 bytes as short)

bytesToSignedNumber(false, 0xF1, 0x04) returns -3836 (2 bytes as short)

bytesToSignedNumber(false, 0x01, 0x01, 0x04) returns 65796 (3 bytes as int)

bytesToSignedNumber(false, 0xF1, 0x01, 0x04) returns 15794436 (3 bytes as int)

bytesToSignedNumber(false, 0xF1, 0x01, 0x01, 0x04) returns -251592444 (4 bytes as int)

bytesToSignedNumber(false, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04) returns 1081146489369067777 (8 of 9 bytes as long)


Another Alternative

From Google

com.google.common.primitives

  Longs.fromByteArray(bytes);
0

精彩评论

暂无评论...
验证码 换一张
取 消