2012-11-02 88 views
4

存储我想读的Java二进制文件。我需要方法来读取无符号的8位值,无符号的16位值和无符号的32位值。什么是最好的(最快,最好看的代码)来做到这一点?我在C++中做到了这一点,做了这样的事情:转换4个字节的32位无符号整数,并在相当长的

uint8_t *buffer; 
uint32_t value = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24; 

但在Java中这将导致一个问题,如果例如缓冲[1]包含有它的符号位设置为左的结果的值shift是一个int(?)。而不是OR:在特定位置只有0xA5,或者:0xFFFFA500或类似的东西,这会“损害”两个最高字节。

我有一个代码,现在看起来像这样:

public long getUInt32() throws EOFException, IOException { 
    byte[] bytes = getBytes(4); 
    long value = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); 
    return value & 0x00000000FFFFFFFFL; 
} 

如果我想这四个字节×67的0xA5 0x72为0x50的结果转换为0xFFFFA567而不是0x5072A567。

编辑:这个伟大的工程:

public long getUInt32() throws EOFException, IOException { 
    byte[] bytes = getBytes(4); 
    long value = bytes[0] & 0xFF; 
    value |= (bytes[1] << 8) & 0xFFFF; 
    value |= (bytes[2] << 16) & 0xFFFFFF; 
    value |= (bytes[3] << 24) & 0xFFFFFFFF; 
    return value; 
} 

但是是不是有更好的办法来做到这一点? 10位操作似乎“有点”多的是这样一个简单的事情。(见我做什么呢?)=)

+0

如果您使用的变量很长,那么ALU将始终以64位执行操作。如果该变量是int,则ALU总是在32位上进行操作(并且使ALU能力的其余32位未被使用)。对一个字节的操作最有可能使ALU的58位不被使用。这些操作总是发生在一个时钟周期内,所以说好的10位的“位”不是太多。 –

+0

不,你的工作实施是完全正确的方法。 –

+1

你不需要上面代码中的最后一个按位和操作:value | =(bytes [3] << 24)&0xFFFFFFFF; –

回答

1

你有正确的想法,我不认为有任何明显的改善。如果你看看java.io.DataInput.readInt spec,他们有相同的代码。他们交换的<<&,但其他标准的顺序。

有没有办法从byte阵列读取一气呵成的int,除非你使用一个内存映射区域,这是方式矫枉过正这一点。

当然,你可以使用一个DataInputStream的,而不是直接读入第一一byte[]:在相对的字节序

DataInputStream d = new DataInputStream(new FileInputStream("myfile")); 
d.readInt(); 

DataInputStream作品不是您正在使用,所以你需要一些Integer.reverseBytes电话也。它不会更快,但它更干净。

2

与样品代码的问题是,当你从字节隐式转换到长,符号扩展,这意味着如果该字节的第一位是1这样做,它垫在长着一个,而不是零。通过使用长转换来防止符号扩展,您的代码可以完美工作。

public static long byteAsULong(byte b) { 
    return ((long)b) & 0x00000000000000FFL; 
} 

public static long getUInt32(byte[] bytes) { 
    long value = byteAsULong(bytes[0]) | (byteAsULong(bytes[1]) << 8) | (byteAsULong(bytes[2]) << 16) | (byteAsULong(bytes[3]) << 24); 
    return value; 
} 

如果您小心,可以使用带符号的值来包含位。您需要避免的是任何形式或有符号的操作,例如算术和有符号的位移。如果你需要将数值打印为数字,请注意,所有内置的java方法都会导致大的无符号数显示为负数。

知道所有的不过最重要的一点,是关于位移位。当向右移动时,>>运营商将维持数字的符号,以2表示恭维。这意味着如果最左边的位是1,则移入的位将是1而不是零。好消息是Java至少有一个无符号字节移位操作,这将始终处于零转移,这是>>>。例如:

int bits; 
bits >>> 4; 

一定要记住,一堆比特表示的数据是任意的。尽管Java的内部方法都将这些位视为二进制的恭维,但如果不使用它们中的任何一个,那么带符号的字节将包含您放入它们的完全相同的位。

1

更普通版本的字节转换成其无符号的数值为整数第一:

public long getUInt32() throws EOFException, IOException { 
    byte[] bytes = getBytes(4); 
    long value = 
     ((bytes[0] & 0xFF) << 0) | 
     ((bytes[1] & 0xFF) << 8) | 
     ((bytes[2] & 0xFF) << 16) | 
     ((bytes[3] & 0xFF) << 24); 
    return value; 
} 

不要搅得位操作的数量就挂了,最有可能的编译器将优化那些字节操作。

此外,您不应该使用long作为32位值以避免出现符号,您可以使用int并忽略它在大部分时间被签名的事实。见this answer

相关问题