将4个字节转换为整数

76
我正在以以下方式读取二进制文件:

InputStream in = new FileInputStream( file );
byte[] buffer = new byte[1024];
while( ( in.read(buffer ) > -1 ) {

   int a = // ??? 
}
我想做的是读取最多4个字节并从中创建一个int值,但我不知道该如何做。
我觉得我需要每次获取4个字节,并执行一个"字节"操作(例如>> << >>&FF之类的操作)来创建新的int。
这个操作的习语是什么?
编辑
哎呀,这有点复杂(解释起来)。
我正在尝试读取一个文件(可能是ascii,二进制,都无所谓),并提取其中可能有的整数。
例如,假设二进制内容(在基数2中):
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000010
整数表示应该是12,对吧? :- / 前32位为1,后32位为2。
11111111 11111111 11111111 11111111

会是 -1

01111111 11111111 11111111 11111111

最大值为 Integer.MAX_VALUE (2147483647)

12个回答

75

ByteBuffer可以处理小端和大端整数。以下是一个示例:


// 读取文件到字节数组
File file = new File("file.bin");
FileInputStream fis = new FileInputStream(file);
byte [] arr = new byte[(int)file.length()];
fis.read(arr);
// 创建一个字节缓冲区并包装数组 ByteBuffer bb = ByteBuffer.wrap(arr);
// 如果文件使用的是小端序而不是网络(big endian, Java 的本机)格式, // 则设置 ByteBuffer 的字节顺序 if(use_little_endian) bb.order(ByteOrder.LITTLE_ENDIAN);
// 使用 ByteBuffer 的 getInt() 方法读取整数。 // 四个字节转换为一个整数! System.out.println(bb.getInt());
希望这有所帮助。

另请参见https://dev59.com/MHE95IYBdhLWcg3wp_yn。 - trashgod

39

如果你已经有了一个byte[]数组,你可以使用:

int result = ByteBuffer.wrap(bytes).getInt();

来源:这里


30

你应该把它放到这样的一个函数中:

public static int toInt(byte[] bytes, int offset) {
  int ret = 0;
  for (int i=0; i<4 && i+offset<bytes.length; i++) {
    ret <<= 8;
    ret |= (int)bytes[i] & 0xFF;
  }
  return ret;
}

例子:

byte[] bytes = new byte[]{-2, -4, -8, -16};
System.out.println(Integer.toBinaryString(toInt(bytes, 0)));

输出:

11111110111111001111100011110000

这样可以处理字节耗尽并正确处理负字节值。

我不知道是否有标准函数可以做到这一点。

需要考虑的问题:

  1. 字节序:不同的CPU架构以不同的顺序放置组成int的字节。根据如何开始使用字节数组,您可能需要担心这个问题;以及
  2. 缓冲:如果您每次获取1024个字节,并从第1022个元素开始一个序列,您将在获得4个字节之前就会达到缓冲区的末尾。最好使用某种形式的缓冲输入流自动进行缓冲,这样您可以重复使用readByte()而不必担心其他问题;
  3. 末尾缓冲区:输入的结尾可能是不均匀的字节(特别是不是4的倍数),这取决于源。但是,如果您创建输入并保证它是4的倍数(或至少是一个前提条件),则可能不需要担心它。

为了进一步说明缓冲的观点,请考虑BufferedInputStream

InputStream in = new BufferedInputStream(new FileInputStream(file), 1024);

现在你有一个InputStream,它会自动缓冲每次1024字节,这样处理起来要轻松得多。这样你可以愉快地每次读取4个字节而不必担心太多的I/O。

其次,你也可以使用DataInputStream

InputStream in = new DataInputStream(new BufferedInputStream(
                     new FileInputStream(file), 1024));
byte b = in.readByte();

或者甚至是:

int i = in.readInt();

并且不必担心构建int


3
你的代码存在一个主要问题——Java中的字节类型是有符号的,因此,如果任何字节的最高位被设置,你的代码将同时在生成的整数中设置所有上面的位。在进行移位或或运算之前,需要对每个字节的上位进行掩码处理,例如:(bytes[0] & 0xff) | ((bytes[1] & 0xff) << 8) | ... - Chris Dodd
@Chris Dodd,感谢您帮我修复了我的网络代码,& 0xFF解决了我的问题!谢谢! - lfxgroove
1
我很不想说,但是你的偏移支持完全失效了。请参见http://ideone.com/uCpovu,我在那里也提供了修复方案。 - quantum
我建议将迭代结构更改为 for (int i = offset; i<4+offset && i<bytes.length; i++) - jackb
1
感谢提供代码片段,我应该指出这里有一个错误 - ret |= (int)bytes[i] & 0xFF; 应该改为 ret |= (int)bytes[i + offset] & 0xFF; - 否则 offset 参数将被完全忽略。 - Ying
显示剩余8条评论

19

只需查看DataInputStream.readInt()的实现方式即可。

    int ch1 = in.read();
    int ch2 = in.read();
    int ch3 = in.read();
    int ch4 = in.read();
    if ((ch1 | ch2 | ch3 | ch4) < 0)
        throw new EOFException();
    return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));

9
需要注意的是,这是针对大端序字节顺序的,而支持小端序只需要做出一个小的改变:返回 ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0))。 - Paul Gregoire
这是不正确的。例如,如果第4个字节等于-1,而其他字节为0,则结果应为255,但实际上是-1。 int k = ((byte)-1) << 0; System.err.println(k); // -1 - Mikhail Ionkin
@MikhailIonkin 您的评论是错误的,而这段代码是正确的。in.read()不会返回一个字节。如果它这样做了,在存储在int变量中时会发生符号扩展。但是in.read()返回流的下一个字节转换为int而不带符号扩展。因此,如果流的下一个字节是0xFF,则in.read()将返回0x000000FF。当您到达流的末尾时,in.read()将返回-1的唯一方法。 - Craig Parton
@CraigParton 是的,但问题是如何转换4个字节,而不是4个整数 - Mikhail Ionkin

5
最简单的方法是:
RandomAccessFile in = new RandomAccessFile("filename", "r"); 
int i = in.readInt();

-- 或 --

DataInputStream in = new DataInputStream(new BufferedInputStream(
    new FileInputStream("filename"))); 
int i = in.readInt();

1
假设他的二进制文件包含大端有符号整数,否则它会失败。非常惨烈。 :) - stmax

4

尝试像这样:

a = buffer[3];
a = a*256 + buffer[2];
a = a*256 + buffer[1];
a = a*256 + buffer[0];

假设最低字节排在前面。如果最高字节排在前面,则需要交换索引(从0到3)。
基本上,对于每个要添加的字节,您首先要将 a 乘以256(相当于向左移动8位),然后再加上新字节。

尽管我在概念上同意Andrey的观点,但我希望任何一个合格的编译器都能够发现并为您修复它。然而,对于这个目的来说,“<<”更清晰。 - Bill K
@Andrey:公平地说,Java编译器可能会自动将x * 256翻译成x << 8 - cletus
取决于编译器的质量 :) - Andrey
不是因为代码“更快”才应该使用<<,而是因为可读性。通过使用<<,可以清楚地表明我们正在进行位运算而不是乘法。实际上,我甚至会将+改为| - Justin

3

这是我使用的简单解决方案:

int value = (a&255)+((b&255)<<8)+((c&255)<<16)+((d&255)<<24);

a是最低有效字节

b是次低有效字节

c是次高有效字节

d是最高有效字节


1

如果要将无符号的4个字节读取为整数,我们应该使用long变量,因为符号位被视为无符号数字的一部分。

long result = (((bytes[0] << 8 & bytes[1]) << 8 & bytes[2]) << 8) & bytes[3]; 
result = result & 0xFFFFFFFF;

这是一个经过充分测试的有效函数


1
for (int i = 0; i < buffer.length; i++)
{
   a = (a << 8) | buffer[i];
   if (i % 3 == 0)
   {
      //a is ready
      a = 0;
   }       
}

1

您还可以使用BigInteger处理可变长度的字节。您可以将其转换为适合您需要的Long、Integer或Short。

new BigInteger(bytes).intValue();

或者表示极性:
new BigInteger(1, bytes).intValue();

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