http://agilejava.blogbus.com/logs/40046103.html
一直以来都在用java编程,以前在Java写一些二进制格式的文件,就用DataOutputStream很方法,例如它的writeInt,writeLong等,我今天在看一些代码的时候发现DataOutputStream在处理多字节的数字的时候,使用的是BIG_ENDIAN(即将高位的字节放在内存地址的低地址上),相应的DataInputStream的读取方式也使用的是BIG_ENDIAN。
这样就引出一个问题,如果我是用Java之外的语言,比如C语言读取由DataOutputStream生成的文件,而平台正好是LITTLE_ENDIAN(用得很广泛的x86的系统都是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的生成二进制数据文件,应该要考虑一下字节顺的问题,以适应一些特殊的需求,比如多语言平台编程的情况。
|