2011-11-14 46 views
4

我需要將數值轉換爲字節數組。例如,長轉換爲字節數組,我有這樣的方法:有沒有解釋這個Java ByteBuffer的行爲?

public static byte[] longToBytes(long l) { 
    ByteBuffer buff = ByteBuffer.allocate(8); 

    buff.order(ByteOrder.BIG_ENDIAN); 

    buff.putLong(l); 

    return buff.array(); 
} 

這是非常簡單的 - 需要很長的,分配,可容納一個數組,並在那裏把它。不管l的值是什麼,我都會得到一個8字節的數組,然後我可以按照預期進行處理和使用。就我而言,我正在創建一個自定義的二進制格式,然後通過網絡傳輸它。

當我調用此方法的值爲773450364時,我得到一個數組[0 0 0 0 46 25 -22 124]回來。我有代碼,也字節數組轉換回它們的數值:

public static Long bytesToLong(byte[] aBytes, int start) { 
    byte[] b = new byte[8]; 

    b[0] = aBytes[start + 0]; 
    b[1] = aBytes[start + 1]; 
    b[2] = aBytes[start + 2]; 
    b[3] = aBytes[start + 3]; 
    b[4] = aBytes[start + 4]; 
    b[5] = aBytes[start + 5]; 
    b[6] = aBytes[start + 6]; 
    b[7] = aBytes[start + 7]; 

    ByteBuffer buf = ByteBuffer.wrap(b); 
return buf.getLong(); 
} 

當我通過從其他方法返回到該方法中,陣列,我得到773450364,這是正確的。

現在,我通過TCP傳輸這個數組到另一個Java客戶端。 java.io.InputStream.read()方法的文檔說,它返回的值爲0到255之間的一個int值,除非到達流的末尾並返回-1。但是,當我使用它來填充字節數組時,我會繼續在接收端獲取負值。我懷疑這與溢出有關(值爲255不能適合Java字節,所以當我把它放入字節數組時,它溢出並變爲負數)。

這給我帶來了我的問題。負數的存在與我有關。現在,我正在開發應用程序的Java端,其中一個字節介於-128和127之間。另一個端點可能是C,C++,Python,Java,C#......誰知道。我不確定某些字節數組中負值的存在是如何影響處理的。 除了記錄這種行爲之外,還有什麼可以/應該如何讓自己和未來的開發人員更容易在本系統上工作,特別是在未使用Java編寫的端點中?

+0

在將'getLong()'調用到'longToBytes'中之前,你不應該在你的'bytesToLong'方法中設置ByteBuffer的字節順序嗎?不是真的與你的問題有關,只是想知道... –

+0

@G_H我應該看看並測試。我其實並沒有自己寫這兩種方法,測試用例也缺乏。感謝您指出了這一點。 –

回答

6

Java中的byte以8位two's complement格式表示。如果您的int範圍在128 - 255之間,並且您將其投射到byte,那麼它將變爲byte,其值爲負值(介於-1和-128之間)。

讀取一個字節後,您必須先檢查它是否爲-1 ,然後再將其轉換爲byte。該方法返回int而不是byte的原因是允許您在將其轉換爲byte之前檢查流結束。

另一件事:你爲什麼要在bytesToLong方法中複製aBytes數組?您可以大大簡化該方法並保存非冗餘副本:

public static Long bytesToLong(byte[] aBytes, int start) { 
    return ByteBuffer.wrap(aBytes, start, 8).order(ByteOrder.BIG_ENDIAN).getLong(); 
} 
1

您的發送和接收端點目前都是用Java實現的。可以想象,您在發送端使用OutputStream,在接收端使用InputStream。假設我們可以暫時相信底層的套接字實現細節,我們會考慮通過套接字發送的任何字節到達其目的地的完全相同。

那麼在將某些東西轉儲到OutputStream時,實際發生在Java級別上的是什麼?當檢查the JavaDoc for a method writing a byte array時,我們看到所有這些都告訴我們字節正在通過流發送。沒有什麼重要的。但是當你檢查文檔method taking an int as argument時,你會看到它詳細說明了這個int是如何寫出來的:低位8位通過流發送爲一個字節,而高位24位(int具有一個Java中的32位表示)簡單地被忽略。

在接收端。你有一個InputStream。除非你使用one of the methods reading directly into a byte array,否則你會得到一個int。 Like the doc says,int可以是介於0和255之間的值,或者如果已到達流的末尾,則爲-1。這是重要的一點。一方面,我們希望每個可能的單字節位模式都可以從InputStream中讀取。但是,我們還必須有一些方法來檢測何時讀取不再能夠返回有意義的值。這就是爲什麼該方法返回一個int而不是一個字節... -1值是該標誌表示流的末尾已達到。如果你得到的不是-1,唯一感興趣的是那些低8位。由於這些可以是任何位模式,因此它們的十進制值的範圍將從-128到127(含)。當你直接讀入一個字節數組而不是整數int時,這個「修整」就會爲你完成。所以你會看到那些負面的價值是有道理的。也就是說,由於Java將字節表示爲帶符號的十進制的方式,它們只是負面的。唯一感興趣的是實際的位模式。對於您所關心的它可能代表值爲0至255或1000至1255

典型的InputStream讀取循環,同時使用一個字節是要去看看這樣的:

InputStream ips = ...; 
int read = 0; 
while((read = ips.read()) != -1) { 
    byte b = (byte)read; 
    //b will now have a bit pattern ranging from 0x00 to 0xff in hex, or -128 to 127 in two-complement signed representation 
} 

運行時,以下(使用Java 7個INT文字)將被照射:

public class Main { 

    public static void main(String[] args) { 

     final int i1 = Ox00_00_00_fe; 
     final int i1 = Ox80_00_00_fe; 

     final byte b1 = (byte)i1; 
     final byte b2 = (byte)i2; 

     System.out.println(i1); 
     System.out.println(i2); 

     System.out.println(b1); 
     System.out.println(b2); 

     final int what = Ox12_34_56_fe; 
     final byte the_f = (byte)what; 

     System.out.println(what); 
     System.out.println(the_f); 

    } 

} 

正如從這個明確的,從int到字節鑄造將簡單地拋棄任何東西,但至少顯著8位。所以int可以是正數或負數,它不會對字節值有任何影響。只有最後8位。

長話短說:你從你的InputStream獲得正確的字節值。這裏真正的擔憂是,如果客戶端可以用任何編程語言編寫並在任何平臺上運行,那麼您需要在文檔中清楚地說明接收到的字節是什麼意思,以及它們是否是long,這是怎麼回事被編碼。請明確指出,編碼是用Java完成的,使用ByteBufferputLong方法以特定的字節順序排列。只有這樣他們才能獲得信息(與Java規範結合)才能完全確定如何解釋這些字節。

0

如果您的所有數據都是big-endian,您可以節省所有這些麻煩並使用DataOutputStream。它擁有你所需要的一切。

+0

不幸的是,這不是所有的Big Endian。 –

相關問題