以前在Java写一些二进制格式的文件,就用DataOutputStream很方法,例如它的 writeInt,writeLong等,我今天在看一些代码的时候发现DataOutputStream在处理多字节的数字的时候,使用的是 BIG_ENDIAN(即将高位的字节放在内存地址的低地址上),相应的DataInputStream的读取方式也使用的是BIG_ENDIAN。
如果我是用C语言读取由DataOutputStream生成的文件,而平台正好是 LITTLE_ENDIAN,很可能会造成数据错误,除非在C程序中自己重新按照 BIG_ENDIAN的格式组装int或者long.
这样我们需要在写文件的时候就按照平台的字节顺来写,而ByteBuffer已经考虑到了这一点。
java.nio.ByteBuffer默认是BIG_ENDIAN(这可能和ByteBuffer主要用来做网络通讯有关),但是这个值是可以修改的。
比较使用DataOutputStream和ByteBuffer写文件的差异:
public static void main(String[] args) throws IOException {
int _int = 12345678;
ByteBuffer _nbuffer = ByteBuffer.allocate(4);
_nbuffer.order(ByteOrder.nativeOrder()); //将新建的ByteBuffer设置为本机的字节顺
_nbuffer.putInt(_int);
_nbuffer.flip();
FileOutputStream _fou = new FileOutputStream("test_dout.data");
FileOutputStream _nfou = new FileOutputStream("test_nbuf.data");
DataOutputStream _dou = new DataOutputStream(_fou);
_dou.writeInt(_int);
_dou.close();
_nfou.write(_nbuffer.array());
_nfou.close();
System.out.println(ByteOrder.nativeOrder());
}
执行上面的代码生成两个文件:
test_dout.data - 使用DataOutputStream生成的BIG_ENDIAN文件,
test_nbuf.data - 使用ByteBuffer生成的主机字节顺的文件(此处的主机字节顺为LITTLE_ENDIAN)
使用下面的C程序分别读取这两个文件:
#include <stdio.h>
int read_file(char* file);
main()
{
char* dout = "test_dout.data";
char* nbuf = "test_nbuf.data";
printf("data in %s:%d\n",dout,read_file(dout));
printf("data in %s:%d\n",nbuf,read_file(nbuf));
}
int read_file(char* file)
{
FILE *fp;
int dat[1];
fp=fopen(file, "rb");
fread(dat, sizeof(int), 1, fp);
fclose(fp);
return dat[0];
}
编译并执行:
gcc a.c
./a.out
data in test_dout.data:1315027968
data in test_nbuf.data:12345678
上面的C程序从test_dout.data取得的int数值是错误的,而从test_nbuf.data是正确的。
ByteBuffer不方便的地方在于它的大小不能自动扩展,但是也是可以解决的,比如MINA自己的ByteBuffer就支持自成扩展。
Java的生成二进制数据文件,应该要考虑一下字节顺的问题,以适应一些特殊的需求,比如多语言平台编程的情况。
如果我是用C语言读取由DataOutputStream生成的文件,而平台正好是 LITTLE_ENDIAN,很可能会造成数据错误,除非在C程序中自己重新按照 BIG_ENDIAN的格式组装int或者long.
这样我们需要在写文件的时候就按照平台的字节顺来写,而ByteBuffer已经考虑到了这一点。
java.nio.ByteBuffer默认是BIG_ENDIAN(这可能和ByteBuffer主要用来做网络通讯有关),但是这个值是可以修改的。
比较使用DataOutputStream和ByteBuffer写文件的差异:
public static void main(String[] args) throws IOException {
int _int = 12345678;
ByteBuffer _nbuffer = ByteBuffer.allocate(4);
_nbuffer.order(ByteOrder.nativeOrder()); //将新建的ByteBuffer设置为本机的字节顺
_nbuffer.putInt(_int);
_nbuffer.flip();
FileOutputStream _fou = new FileOutputStream("test_dout.data");
FileOutputStream _nfou = new FileOutputStream("test_nbuf.data");
DataOutputStream _dou = new DataOutputStream(_fou);
_dou.writeInt(_int);
_dou.close();
_nfou.write(_nbuffer.array());
_nfou.close();
System.out.println(ByteOrder.nativeOrder());
}
执行上面的代码生成两个文件:
test_dout.data - 使用DataOutputStream生成的BIG_ENDIAN文件,
test_nbuf.data - 使用ByteBuffer生成的主机字节顺的文件(此处的主机字节顺为LITTLE_ENDIAN)
使用下面的C程序分别读取这两个文件:
#include <stdio.h>
int read_file(char* file);
main()
{
char* dout = "test_dout.data";
char* nbuf = "test_nbuf.data";
printf("data in %s:%d\n",dout,read_file(dout));
printf("data in %s:%d\n",nbuf,read_file(nbuf));
}
int read_file(char* file)
{
FILE *fp;
int dat[1];
fp=fopen(file, "rb");
fread(dat, sizeof(int), 1, fp);
fclose(fp);
return dat[0];
}
编译并执行:
gcc a.c
./a.out
data in test_dout.data:1315027968
data in test_nbuf.data:12345678
上面的C程序从test_dout.data取得的int数值是错误的,而从test_nbuf.data是正确的。
ByteBuffer不方便的地方在于它的大小不能自动扩展,但是也是可以解决的,比如MINA自己的ByteBuffer就支持自成扩展。
Java的生成二进制数据文件,应该要考虑一下字节顺的问题,以适应一些特殊的需求,比如多语言平台编程的情况。