Java实现较大二进制文件的读、写方法
程序员文章站
2024-03-05 23:05:01
由于项目需要,需要对二进制文件进行读写、转换。
文件说明:由其他程序得到的二进制文件,文件内容为:包含23543个三角形、13270个顶点的三角网所对应的721组流速矢量...
由于项目需要,需要对二进制文件进行读写、转换。
文件说明:由其他程序得到的二进制文件,文件内容为:包含23543个三角形、13270个顶点的三角网所对应的721组流速矢量(u、v)文件,通俗些说,一条数据包含两个双精度型的数值,每组数组包含23543条数据,如果以一个双精度数值为单位,则总共有23543 * 721 * 2 =33,949,006条数据。由fortran程序以每 8 byte存储一个数值的二进制文件存储,最终文件大小为下图所示:
测试:从该文件读出数据之后,转换为十进制,存储到另一个文件中。
/** * 针对大文件存储,请依次调用beginsave、addsave、endsave。 * * @author ck * */ public class datautil { dataoutputstream binaryout=null; bufferedwriter textout=null; string filepath=null; enum savefiletype{text,binary}; savefiletype savefiletype; /** * double转byte[] * * @param d * @return */ public static byte[] double2bytes(double d) { long value = double.doubletorawlongbits(d); byte[] byteret = new byte[8]; for (int i = 0; i < 8; i++) { byteret[i] = (byte) ((value >> 8 * i) & 0xff); } return byteret; } /** * byte[]转double * * @param arr * @return */ public static double bytes2double(byte[] arr) { long value = 0; for (int i = 0; i < 8; i++) { value |= ((long) (arr[i] & 0xff)) << (8 * i); } return double.longbitstodouble(value); } /** * 大型数据存储之开始存储 * @param filepath 文件路径 * @param savefiletype 保存的文件类型,文本文件、双精度所存的二进制文件 * @return * @throws ioexception */ public boolean beginsave(string filepath,savefiletype savefiletype) throws ioexception { if (filepath == "" || filepath == null) { system.out.println("the savepath is null."); return false; } this.filepath=filepath; this.savefiletype=savefiletype; file datafile = new file(filepath); if (!datafile.getparentfile().exists()) { datafile.getparentfile().mkdirs(); } if (datafile.exists()) { datafile.delete(); } datafile.createnewfile(); switch(this.savefiletype){ case text: textout= new bufferedwriter(new filewriter(datafile,true)); break; case binary: binaryout = new dataoutputstream(new fileoutputstream(datafile,true)); break; default: break; } return true; } /** * 大型文件存储之追加存储 * @param datastr 若是文本存储则无要求,若是双精度的二进制文件,以若干空格隔开 * @return * @throws ioexception */ public boolean addsave(string datastr) throws ioexception{ switch(this.savefiletype){ case text: this.textout.append(datastr); break; case binary: datastr=datastr.trim(); string[] dataarray=datastr.split("\\s+"); for(int i=0;i<dataarray.length;i++){ this.binaryout.write(double2bytes(double.parsedouble(dataarray[i]))); } break; default: break; } return true; } /** * 大型文件存储之结束保存,清空缓存、关闭文件。 * @return * @throws ioexception */ public boolean endsave() throws ioexception{ switch(this.savefiletype){ case text: this.textout.flush(); this.textout.close(); break; case binary: this.binaryout.flush(); this.binaryout.close(); break; default: break; } return true; } /** * 将字符串保存为文本文件(一次完成) * * @param datastr * 文件内容 * @param savepath * 文件路径,包含文件名、后缀 * @return * @throws ioexception */ public boolean savetextfile(string datastr, string savepath) throws ioexception { if (datastr == "" || datastr == null) { system.out.println("the datastr is null."); return false; } if (savepath == "" || savepath == null) { system.out.println("the savepath is null."); return false; } file datafile = new file(savepath); if (!datafile.getparentfile().exists()) { datafile.getparentfile().mkdirs(); } if (datafile.exists()) { datafile.delete(); } datafile.createnewfile(); bufferedwriter out; out = new bufferedwriter(new filewriter(datafile)); out.append(datastr); out.flush(); out.close(); return true; } /** * 双精度存为二进制数据(一次存储) * * @param datastr 双精度数据组成的字符串,以若干空格隔开 * @param outputpath * @return * @throws ioexception */ public boolean savebinaryfile(string datastr, string outputpath) throws ioexception { if (datastr == "" || datastr == null) { system.out.println("the datastr is null."); return false; } if (outputpath == "" || outputpath == null) { system.out.println("the outputpath is null."); return false; } file datafile = new file(outputpath); if (!datafile.getparentfile().exists()) { datafile.getparentfile().mkdirs(); } if (datafile.exists()) { datafile.delete(); } datafile.createnewfile(); dataoutputstream out; out = new dataoutputstream(new fileoutputstream(datafile)); // 数据处理 datastr=datastr.trim(); string[] dataarray=datastr.split("\\s+"); for(int i=0;i<dataarray.length;i++){ out.write(double2bytes(double.parsedouble(dataarray[i]))); } out.flush(); out.close(); return true; } }
代码说明:其中byte[]与double互转为在互联网上查到的方法,具体是哪位大神的我忘记了,在这里为了记录就贴出来啦,上述代码包含了处理小型文件时,将所有内容存在缓存中,之后再一次性写入文本文件、二进制文件中的方法,还包含了对较大型文件的读写方法,下面是自己的一个读写测试。
/** * 测试二进制大文件读写(200m左右) * @author ck * */ public class filetest { static string inputfilepath=""; //输入文件路径,包含文件名后缀 static string outputfilepath=""; //输出文件名,包含文件名后缀 public static void file2file() throws ioexception{ datautil datautil=new datautil(); datainputstream br=new datainputstream( new bufferedinputstream( new fileinputstream(inputfilepath))); datautil.beginsave(outputfilepath, savefiletype.text); //初始化,创建文件,采用文件追加存储的思路 byte[] onedata=new byte[8]; int i=0,count =0 ; while(br.read(onedata, 0, 8)!=-1){ i=i+1; datautil.addsave(string.valueof(datautil.bytes2double(onedata))); if(i/23543==0){ count++; system.out.println(count+"\n"); } } datautil.endsave(); //将还在缓存中的数据写入到文件中,关闭文件。 } }
此次测试代码很快就run完了,但是输出文件的生成大概用了近半分钟(刻意秒表计时了一次),尝试用一次性读写的办法,卡很久,也没有出结果。所得的十进制文本文件,大小为这么多:
我想,原来fortran程序作者的初衷应该是觉得二进制存储比十进制节省空间吧,事实上也确实节省了一半多的空间。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 数组工具类Arrays