簡體   English   中英

將4個字節轉換為無符號的32位整數並將其存儲為long

[英]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;
}

如果我想轉換四個字節0​​x67 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM