在Java中将字节数组转换为整数,反之亦然


139

我想将一些数据存储到Java中的字节数组中。基本上只是数字,每个数字最多占用2个字节。

我想知道如何将整数转换为2字节长的字节数组,反之亦然。我发现有很多解决方案在使用Google搜索,但是大多数解决方案没有解释代码中会发生什么。我不了解很多变化的东西,所以我希望能得到一个基本的解释。


4
多少你了解了位移位?听起来好像问题确实是“移位有什么作用”,而不是关于转换为字节数组的问题,实际上-如果您确实想了解转换的工作方式。
乔恩·斯基特

1
(为澄清起见,我对任何一个问题都没问题,但是有必要弄清楚您真正想回答哪个问题。这样一来,您很可能会得到更有用的答案。)
乔恩·斯凯特

好吧,我明白了!谢谢你的发言。我知道我只是不了解它在转换字节数组中使用了什么位移。
克里斯,

3
@prekageo和Jeff Mercado感谢您的两个回答。prekageo给了一个很好的解释,很好的链接!这使我更清楚。Jeff Mercados解决方案解决了我遇到的问题。
克里斯(Chris)

Answers:


230

使用在java.nio命名空间中找到的类,尤其是ByteBuffer。。它可以为您完成所有工作。

byte[] arr = { 0x00, 0x01 };
ByteBuffer wrapped = ByteBuffer.wrap(arr); // big-endian by default
short num = wrapped.getShort(); // 1

ByteBuffer dbuf = ByteBuffer.allocate(2);
dbuf.putShort(num);
byte[] bytes = dbuf.array(); // { 0, 1 }

2
如果字节数组仅包含1或2个整数,是否太昂贵了?不确定建立一个的成本ByteBuffer
喵猫2012年

您多久使用2-4字节块的二进制数据?真?明智的实现方式可以在BUFSIZ块(通常为4kb)中使用它,也可以使用其他IO库来隐藏此细节。框架中有一个完整的库,专用于帮助您处理数据缓冲区。在没有充分理由(例如perf或其他关键操作)的情况下实施常见操作时,会对自己和代码的其他维护者造成损害。这些缓冲区仅仅是对数组进行操作的包装器,仅此而已。
杰夫·梅卡多

您怎么能实例化一个抽象类?

1
@JaveneCPPMcGowan这个答案中没有直接实例化。如果您的意思是工厂方法wrapallocate,它们不会返回抽象类的实例ByteBuffer
Marko Topolnik

不是3字节跨步的解决方案。我们可以得到CharShortInt。我想我可以填充到4个字节,每次都丢弃第4个字节,但是我宁愿不这样做。
John

128
byte[] toByteArray(int value) {
     return  ByteBuffer.allocate(4).putInt(value).array();
}

byte[] toByteArray(int value) {
    return new byte[] { 
        (byte)(value >> 24),
        (byte)(value >> 16),
        (byte)(value >> 8),
        (byte)value };
}

int fromByteArray(byte[] bytes) {
     return ByteBuffer.wrap(bytes).getInt();
}
// packing an array of 4 bytes to an int, big endian, minimal parentheses
// operator precedence: <<, &, | 
// when operators of equal precedence (here bitwise OR) appear in the same expression, they are evaluated from left to right
int fromByteArray(byte[] bytes) {
     return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
}

// packing an array of 4 bytes to an int, big endian, clean code
int fromByteArray(byte[] bytes) {
     return ((bytes[0] & 0xFF) << 24) | 
            ((bytes[1] & 0xFF) << 16) | 
            ((bytes[2] & 0xFF) << 8 ) | 
            ((bytes[3] & 0xFF) << 0 );
}

将带符号的字节打包为一个int时,每个字节都需要屏蔽掉,因为由于算术提升规则(在JLS,转换和提升中所述),它被符号扩展为32位(而不是零扩展)。

Joshua Bloch和Neal Gafter在Java Puzzlers(“每个字节都有很大的乐趣”)中描述了一个与此有趣的难题。在将字节值与int值进行比较时,将字节符号扩展为int,然后将该值与另一个int进行比较

byte[] bytes = (…)
if (bytes[0] == 0xFF) {
   // dead code, bytes[0] is in the range [-128,127] and thus never equal to 255
}

请注意,所有数字类型都用Java签名,但char是16位无符号整数类型。


我认为& 0xFFs是不必要的。
Ori Popowski

11
@LeifEricson我认为& 0xFFs是必需的,因为它告诉JVM将带符号的字节转换为只设置了那些位的整数。否则,字节-1(0xFF)将变成整数-1(0xFFFFFFFF)。我可能是错的,即使我不在意也不会使事情变得更清晰。
coderforlife 2014年

4
&0xFF确实是强制性的。 byte b = 0; b |= 0x88; System.out.println(Integer.toString(b, 16)); //Output: -78 System.out.println(Integer.toString(b & 0xFF, 16)); //Output: 88
HBN 2014年

1
@ptntialunrlsd并非如此。在执行之前上操作byte使用0xFF( int),JVM 定投byte,以int1扩展0扩展根据位领导第一。Java中没有无符号字节bytes总是带符号的。
尼尔

2
从byte数组解析int时,请注意byte数组的大小,如果它大于4个字节,则根据ByteBuffer.getInt():的文档,Reads the next four bytes at this buffer's current position将仅解析前4个字节,这不是您想要的。
Bin

56

您也可以将BigInteger用于可变长度的字节。您可以根据需要将其转换为long,int或short。

new BigInteger(bytes).intValue();

或表示极性:

new BigInteger(1, bytes).intValue();

只需返回字节:

new BigInteger(bytes).toByteArray()

1
请注意,从1.8开始,不是intValueExact,而是intValue
Abhijit Sarkar,

5

一个基本的实现将是这样的:

public class Test {
    public static void main(String[] args) {
        int[] input = new int[] { 0x1234, 0x5678, 0x9abc };
        byte[] output = new byte[input.length * 2];

        for (int i = 0, j = 0; i < input.length; i++, j+=2) {
            output[j] = (byte)(input[i] & 0xff);
            output[j+1] = (byte)((input[i] >> 8) & 0xff);
        }

        for (int i = 0; i < output.length; i++)
            System.out.format("%02x\n",output[i]);
    }
}

为了了解事物,您可以阅读以下WP文章:http : //en.wikipedia.org/wiki/Endianness

上面的源代码将输出34 12 78 56 bc 9a。前2个字节(34 12)表示第一个整数,依此类推。上面的源代码以little endian格式编码整数。


2
/** length should be less than 4 (for int) **/
public long byteToInt(byte[] bytes, int length) {
        int val = 0;
        if(length>4) throw new RuntimeException("Too big to fit in int");
        for (int i = 0; i < length; i++) {
            val=val<<8;
            val=val|(bytes[i] & 0xFF);
        }
        return val;
    }

0

某人有一个要求,他们必须从位中读取数据,可以说您只需要从3位中读取数据,但是您需要带符号的整数,然后使用以下代码:

data is of type: java.util.BitSet

new BigInteger(data.toByteArray).intValue() << 32 - 3 >> 32 - 3

幻数3可以替换为您使用的位数(不是字节)


0

由于经常,番石榴您的需求。

要从字节数组转到int Ints.fromBytesArray:,请在此处 doc

要从int转换为字节数组:Ints.toByteArray,请在此处 doc


-7

我认为这是转换为int的最佳模式

   public int ByteToint(Byte B){
        String comb;
        int out=0;
        comb=B+"";
        salida= Integer.parseInt(comb);
        out=out+128;
        return out;
    }

第一个将字节转换为String

comb=B+"";

下一步将转换为int

out= Integer.parseInt(comb);

但是出于这个原因,字节的范围是-128至127,我认为最好使用范围0至255,您只需要这样做:

out=out+256;

错了 考虑字节0x01。您的方法将输出129,这是错误的。0x01应该输出整数1。如果从parseInt获得的整数为负,则仅应加128。
disklosr 2014年

我的意思是您应该添加256而不是128。此后无法对其进行编辑。
disklosr 2014年

更改了帖子以添加256,因为它可能对其他人有用!
apmartin1991

这会进行大量转换并创建新对象(请考虑对for循环执行此操作),这可能会降低性能,请检查Integer.toString()方法以获取有关如何解析数字的提示。
Marcos Vasconcelos 2015年

同样,在stackoverflow上发布代码时,关键是要发布易于理解的代码。容易理解的代码必须具有可理解的标识符。在stackoverflow上,可理解的语言必定是英语
Mike Nakis
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.