Preface:
考慮有下面 C 代碼從檔案讀入 Binary 資料並轉為對應 struct 結構 "mystruct":
- #include
- #include
- struct mystruct
- {
- unsigned char x;
- unsigned short y;
- unsigned int z;
- };
- int main(void)
- {
- FILE* myfile;
- struct mystruct ms;
- myfile = fopen("data.bin", "rb");
- if(!myfile)
- {
- printf("Unable to open the binary!\n");
- return EXIT_FAILURE;
- }
- fread(&ms, sizeof(struct mystruct), 1, myfile);
- printf("mystruct size=%x\n", sizeof(ms));
- printf("unsigned byte=%x (%d)\n", ms.x, sizeof(ms.x));
- printf("unsigned short=%x (%d)\n", ms.y, sizeof(ms.y));
- printf("unsigned int=%u (%d)\n", ms.z, sizeof(ms.z));
- fclose(myfile);
- return EXIT_SUCCESS;
- }
Example:
首先根據 C 資料結構 定義 unsigned char 佔 1 個 byte; unsigned short 佔 2 個 bytes; unsigned int 佔 2~4 bytes (我的機器是 4 bytes). 因此我們可以推知 mystruct 結構應該佔 1+2+4=7 個 bytes. 接著來看 Java 代碼如何產生 mystruct 的 binary 檔案:
- NativeDataOutputStream.java
- import java.nio.ByteBuffer
- import java.nio.ByteOrder
- class NativeDataOutputStream extends FilterOutputStream{
- public NativeDataOutputStream(){
- }
- public NativeDataOutputStream(OutputStream out) {
- super(out);
- }
- public void writeShort(short value) throws IOException {
- ByteBuffer buffer = ByteBuffer.allocate(2).order(ByteOrder.nativeOrder());
- buffer.putShort(value);
- out.write(buffer.array());
- }
- public void writeInt(int value) throws IOException {
- ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.nativeOrder());
- buffer.putInt(value);
- out.write(buffer.array());
- }
- public void writeLong(long value) throws IOException {
- ByteBuffer buffer = ByteBuffer.allocate(8).order(ByteOrder.nativeOrder());
- buffer.putLong(value);
- out.write(buffer.array());
- }
- static void main(args) {
- short unsignedByte = 1;
- int unsignedShort = 2;
- long unsignedInt = 3;
- NativeDataOutputStream out = new NativeDataOutputStream(new BufferedOutputStream(new FileOutputStream("data.bin", false)));
- try {
- out.write ((byte) unsignedByte );
- out.writeShort((short) unsignedShort);
- out.writeInt ((int) unsignedInt );
- out.flush();
- } finally {
- out.close();
- }
- }
- }
接著使用 C 編譯完的程式 a.out 去讀 data.bin, 這時問題發生了:
由 unsigned short=100 可以知道系統是 Big-Endian, 但是問題在於 unsigned int 的值居然是 0!!!! 後來知道 struct 在 compiler 的 align purpose 之下會自動加 padding:
於是我使用下面代碼使用 C 代碼輸出 mystruct 的 binary 資料:
- #include
- struct mystruct{
- unsigned char x;
- unsigned short y;
- unsigned int z;
- };
- int main()
- {
- FILE *pFile;
- pFile = fopen("data.bin", "wb");
- if(!pFile)
- {
- printf("Fail to open file!\n");
- return -1;
- }
- struct mystruct s = {1,1,1};
- fwrite(&s, sizeof(s), 1, pFile);
- fclose(pFile);
- return 0;
- }
因此由上可以知道當輸出 byte 時, 需要加上 padding "BC", 有了這個發現, 我們可以改寫 Java 代碼在 writeByte 的地方:
- public void writeByte(byte value) throws IOException
- {
- if(bo.equals(ByteOrder.BIG_ENDIAN))
- {
- out.write(value);
- out.write(HexByteKit.Hex2Byte("BC")); // Add Padding
- }
- else
- {
- out.write(HexByteKit.Hex2Byte("BC")); // Add Padding
- out.write(value);
- }
- }
此時使用 C 讀入 data.bin 的結果就正常了:
Supplement:
* flib - HexByteKit
* Java Primate Data Type
沒有留言:
張貼留言