c和java中的二进制文件

来源:互联网 发布:bt云播软件 编辑:程序博客网 时间:2024/05/17 06:42
最近需要写一段程序,完成以下的工作,用java将数据以二进制的形式写入文件中,然后用C读出此二进制文件。

开始的时候没有考虑机器的字节序,直接搞出segment fault.想了很久,才明白原来是java和C的字节序是不一样的。

java中的字节序是big endian的,它是与机器无关的。而c的字机序是机器相关的,而当前用的机器是x84_64结构,是little endian的,
所以出错就不足为奇了。

背景知识:

主机字节序

不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序
最常见的有两种
1. Little endian:将低序字节存储在起始地址
2. Big endian:将高序字节存储在起始地址

LE little-endian
最符合人的思维的字节序
地址低位存储值的低位
地址高位存储值的高位
怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说
低位值小,就应该放在内存地址小的地方,也即内存地址低位
反之,高位值就应该放在内存地址大的地方,也即内存地址高位

BE big-endian
最直观的字节序
地址低位存储值的高位
地址高位存储值的低位
为什么说直观,不要考虑对应关系
只需要把内存地址从左到右按照由低到高的顺序写出
把值按照通常的高位到低位的顺序写出
两者对照,一个字节一个字节的填充进去

例子:在内存中双字0x01020304(DWORD)的存储方式

内存地址
4000 4001 4002 4003
LE 04 03 02 01
BE 01 02 03 04

例子:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x23 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
x86系列CPU都是little-endian的字节序.

网络字节序
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。

为了进行转换 bsd socket提供了转换的函数 有下面四个
htons 把unsigned short类型从主机序转换到网络序
htonl 把unsigned long类型从主机序转换到网络序
ntohs 把unsigned short类型从网络序转换到主机序
ntohl 把unsigned long类型从网络序转换到主机序

在使用little endian的系统中 这些函数会把字节序进行转换
在使用big endian类型的系统中 这些函数会定义成空宏

同样 在网络程序开发时 或是跨平台开发时 也应该注意保证只用一种字节序 不然两方的解释不一样就会产生bug.

注:
1、网络与主机字节转换函数:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)
2、不同的CPU上运行不同的操作系统,字节序也是不同的,参见下表。
处理器 操作系统 字节排序
Alpha 全部 Little endian
HP-PA NT Little endian
HP-PA UNIX Big endian
Intelx86 全部 Little endian <-----x86系统是小端字节序系统
Motorola680x() 全部 Big endian
MIPS NT Little endian
MIPS UNIX Big endian
PowerPC NT Little endian
PowerPC 非NT Big endian <-----PPC系统是大端字节序系统
RS/6000 UNIX Big endian
SPARC UNIX Big endian
IXP1200 ARM核心 全部 Little endian

Java字节序
java语言是sun搞出来了,自然得跟sparc机器一样,属于big endian,需要说明的是,它是跟机器无关的,这点由JVM来保证,所以保证了
你一次编写,处处运行

java中将二进制转化成little endian

说了这么多废话,回到主题,怎样在java程序中用将二进制格式转化成little endian了,主要是用到了byte数组,好了,废话不多说,直接
上程序

public static int convertIntBigToLittle(int data){

int offset = 0;
byte [] array = new byte[4];

array[offset+3] = (byte)((data >>> 24) & 0xFF);
array[offset+2] = (byte)((data >>> 16) & 0xFF);
array[offset+1] = (byte)((data >>> 8) & 0xFF);
array[offset] = (byte)((data >>> 0) & 0xFF);

int ch1 = array[offset];
int ch2 = array[offset+1];
int ch3 = array[offset+2];
int ch4 = array[offset+3];
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}

public static long convertLongBigToLittle( long data) {

int offset = 0;
byte [] array = new byte[8];

array[offset+7] = (byte)(data >>> 56);
array[offset+6] = (byte)(data >>> 48);
array[offset+5] = (byte)(data >>> 40);
array[offset+4] = (byte)(data >>> 32);
array[offset+3] = (byte)(data >>> 24);
array[offset+2] = (byte)(data >>> 16);
array[offset+1] = (byte)(data >>> 8);
array[offset+0] = (byte)(data >>> 0);

return (((long)array[offset] << 56) +
((long)(array[offset+1] & 255) << 48) +
((long)(array[offset+2] & 255) << 40) +
((long)(array[offset+3] & 255) << 32) +
((long)(array[offset+4] & 255) << 24) +
((array[offset+5] & 255) << 16) +
((array[offset+6] & 255) << 8) +
((array[offset+7] & 255) << 0));
}
原创粉丝点击