Java 中的 Endianness 問題處理

終於瞭解不同 Endianness 在不同機器上會發生的狀況了。

Endianness 在計組中所指的是在記憶體中,以數個 byte 來表示特定資料時,這些 byte 的排列方式,因此 Endianness 也稱做 byte order。

ex: 假設一個 data 需以 ABCD 四個 byte 來表示︰

long data = 0x11223344; // A:0x11 B:0x22 C:0x33 D:0x44

在 memory 中,你可以直觀的用 ABCD 排列來表示 data (此種方式稱為 Big Endian)︰也可以使用 DCBA 的排列來表示 (此種方式稱為 Little Endian)。

而 Endianness 的選擇也會影響噹我們將資料寫入檔案,與將資料自檔案中讀取出來的 byte order 解讀方式。使用 big-endian 儲存的文件改用 little-endian 方式來解讀,結果就不會正確。

由上面的解釋我們可以瞭解,Endianness 的選擇取決於電腦硬體設計者,以目前市面上常見的 CPU 來看,x86 CPU 體系採用 little-endian,而大部份的 RISC CPU 則是採用 big-endian。

而 JVM 這個 "Virtual Machine" 呢,則是採用 big-endian,這也就造成了,當使用 Java 所儲存的 big-endian 文件,在使用 little-endian 下的 PC 程式開啟時會有問題;而在 PC 中以程式儲存的 little-endian 文件,在 Java 中讀取時資料也會解讀錯誤。

講了這麼多,其實我只是要記錄一下解決的辦法 XD

以 Short 資料型別(1 Short = 2 Byte)為例,當我在 Java 中要讀取一個 little-endian 的文件時(大部份 PC 程式所產生的文件皆是),有兩種方式︰

第一種方法為個別讀取兩個 byte,然後將兩個 byte 的順序對調,再重組出一個 big-endian 的 Short 型別資料,方法如下︰

byte b[] = new byte[2];
RandomAccessFile input = new RandomAccessFile(sampleFile, "r" );
int sampleNum = (int)input.length()/2;
samples = new short[sampleNum];
for(int k = 0; k < samples.length; k++) {
    b[1] = input.readByte();
    b[0] = input.readByte();
    samples[k] = ByteBuffer.wrap(b).getShort();
}
input.close();

第二種方法則是使用 Short 內建有的 reverseBytes() method 做 byte 倒轉的動作︰

RandomAccessFile input = new RandomAccessFile(sampleFile, "r" );
int sampleNum = (int)input.length()/2;
samples = new short[sampleNum];
for(int k = 0; k < samples.length; k++) {
    samples[k] = Short.reverseBytes(input.readShort());
}
input.close();

以上兩種方式都可以解決 Java 下 Endianness 的問題。

參考資料︰

0 意見: