[英]Convert 4 bytes to an unsigned 32-bit integer and storing it in a long
我正在嘗試用Java讀取二進制文件。 我需要讀取無符號8位值,無符號16位值和無符號32位值的方法。 這樣做的最佳(最快,最好看的代碼)是什么? 我用c ++完成了這個並做了類似這樣的事情:
uint8_t *buffer;
uint32_t value = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24;
但是在Java中,這會導致一個問題,例如,如果緩沖區[1]包含一個值,該值設置了符號位,因為左移的結果是一個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;
}
如果我想轉換四個字節0x67 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位操作看起來很“有點”..(看看我在那里做了什么?)=)
示例代碼的問題在於,當您從字節隱式轉換為long時,使用符號擴展進行轉換,這意味着如果字節的第一位為1,則使用1而不是零填充long。 通過使用轉換為long來阻止符號擴展,您的代碼可以完美地運行。
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方法都會導致大的無符號數字顯示為負數。
最重要的是要了解所有這些,關於位移。 當向右移動時, >>
操作符將保持數字符號的兩個贊美。 這意味着如果最左邊的位是1,則移入的位將是1而不是0。 好消息是Java至少有一個無符號位移位運算符,它總是以零移位,它是>>>
。 例:
int bits;
bits >>> 4;
永遠記住一堆比特表達的數據是任意的。 即使Java的內部方法都將這些位視為兩個恭維,但如果不使用它們中的任何一個,則有符號字節包含與放入它們完全相同的位。
你有正確的想法,我認為沒有任何明顯的改善。 如果你看一下java.io.DataInput.readInt
規范 ,它們就有相同的代碼。 它們切換<<
和&
的順序,但是否則是標准的。
有沒有辦法讀一個int
從一氣呵成byte
數組,除非你使用一個內存映射區域,這是矯枉過正的方式為這個。
當然,您可以直接使用DataInputStream
而不是先讀入byte[]
:
DataInputStream d = new DataInputStream(new FileInputStream("myfile"));
d.readInt();
DataInputStream
工作方式與您使用的相反,因此您還需要一些Integer.reverseBytes
調用。 它不會更快,但它更清潔。
更常規的版本首先將字節轉換為無符號值作為整數:
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
並忽略它在大多數時候都被簽名的事實。 看到這個答案 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.