【问题标题】:Convert a byte array to integer in Java and vice versa在Java中将字节数组转换为整数,反之亦然
【发布时间】:2011-11-28 23:06:44
【问题描述】:

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

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

【问题讨论】:

  • 您对位移了解多少?听起来问题实际上是“位移有什么作用”而不是转换为字节数组,真的 - 如果您真的想了解转换是如何工作的。
  • (澄清一下,我对任何一个问题都很好,但值得明确哪个你真正想要回答的问题。你可能会得到一个答案这样对你更有用。)
  • 好的,我明白了!谢谢你的评论。我知道移位是什么,只是还不明白它在转换字节数组中的用途。
  • @prekageo 和 Jeff Mercado 感谢您的两个回答。 prekageo 很好地解释了这是如何完成的,很好的链接!这让我更清楚。 Jeff Mercados 解决方案解决了我遇到的问题。

标签: java types endianness


【解决方案1】:

通常,guava 可以满足您的需求。

从字节数组到整数:Ints.fromBytesArray, doc here

从 int 到字节数组:Ints.toByteArray, doc here

【讨论】:

    【解决方案2】:
    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(“A Big Delight in Every Byte”)中描述了一个与此相关的有趣谜题。将字节值与 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
    }
    

    请注意,除了 char 是 16 位无符号整数类型之外,所有数字类型都在 Java 中进行了签名。

    【讨论】:

    • 我认为&amp; 0xFFs 是不必要的。
    • @LeifEricson 我相信&amp; 0xFFs 是必要的,因为它告诉JVM 将带符号的字节转换为仅设置了这些位的整数。否则字节 -1 (0xFF) 将变成 int -1 (0xFFFFFFFF)。我可能是错的,即使我是错的,它也不会造成伤害,而且会让事情变得更清晰。
    • & 0xFF 确实是强制性的。 byte b = 0; b |= 0x88; System.out.println(Integer.toString(b, 16)); //Output: -78 System.out.println(Integer.toString(b &amp; 0xFF, 16)); //Output: 88
    • @ptntialunrlsd 实际上不是。在使用 0xFF (int) 对 byte 执行 & 操作之前,JVM 会将 byte 强制转换为 int,并使用 1 扩展0首先根据前导位扩展。 Java 中没有无符号字节bytes 总是有符号的。
    • 从字节数组解析int时,注意字节数组的大小,如果大于4字节,根据ByteBuffer.getInt()的doc:Reads the next four bytes at this buffer's current position,只有前4字节将被解析,这不应该是你想要的。
    【解决方案3】:

    有人要求他们必须从位中读取,假设您只能从 3 位中读取,但您需要有符号整数,然后使用以下内容:

    data is of type: java.util.BitSet
    
    new BigInteger(data.toByteArray).intValue() << 32 - 3 >> 32 - 3
    

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

    【讨论】:

      【解决方案4】:
      /** 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;
          }
      

      【讨论】:

        【解决方案5】:

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

        new BigInteger(bytes).intValue();
        

        或表示极性:

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

        只取回字节:

        new BigInteger(bytes).toByteArray()
        

        【讨论】:

        • 请注意,从 1.8 开始,它是 intValueExact,而不是 intValue
        【解决方案6】:

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

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

        第一个字节转换为字符串

        comb=B+"";
        

        下一步是转换为 int

        out= Integer.parseInt(comb);
        

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

        out=out+256;
        

        【讨论】:

        • 这是错误的。考虑字节 0x01。您的方法将输出错误的 129。 0x01 应该输出整数 1。如果从 parseInt 获得的整数是负数,则应该只添加 128。
        • 我的意思是你应该添加 256 而不是 128。之后无法编辑。
        • 更改帖子以添加 256,因为它可能对其他人有用!
        • 这会进行大量转换并创建新对象(考虑在 for 循环中这样做),这会降低性能,请检查 Integer.toString() 方法以获取有关如何解析数字的提示。
        • 另外,在 stackoverflow 上发布代码时,重点是发布易于理解的代码。易于理解的代码必须具有可理解的标识符。而在stackoverflow上,understandable必然意味着英文
        【解决方案7】:

        使用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 }
        

        【讨论】:

        • 如果字节数组只包含1或2个整数是不是太贵了?不确定构建ByteBuffer 的成本。
        • 您使用 2-4 字节块的二进制数据的频率如何?真的吗?一个理智的实现要么在 BUFSIZ 块(通常为 4kb)中使用它,要么使用隐藏此细节的其他 IO 库。框架中有一个完整的库,专门用于帮助您处理数据缓冲区。当您在没有充分理由的情况下实施常见操作(无论是性能还是其他关键操作)时,您会对您自己和代码的其他维护者造成伤害。这些缓冲区只是对数组进行操作的包装器,仅此而已。
        • 你怎么能实例化一个抽象类?
        • @JaveneCPPMcGowan 此答案中不存在直接实例化。如果您指的是工厂方法wrapallocate,它们不会返回抽象类ByteBuffer 的实例。
        • 不是 3 字节跨度的解决方案。我们可以得到CharShortInt。我想我可以每次填充到 4 个字节并丢弃第 4 个字节,但我宁愿不这样做。
        【解决方案8】:

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

        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)代表第一个整数等。上面的源代码以小端格式对整数进行编码。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-02-16
          • 1970-01-01
          • 2013-11-11
          • 2013-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多