2012-11-02 90 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

相關問題