现有如下的一个需求,向已存在1G数据的txt文本里末尾追加一行文字,内容如下“Lucene是一款非常优秀的全文检索库”。可能大多数朋友会觉得这个需求很easy,说实话,确实easy,然后XXX君开始实现了,直接使用Java中的流读取了txt文本里原来所有的数据转成字符串后,然后拼接了“Lucene是一款非常优秀的全文检索库”,又写回文本里了,至此,大功告成。后来需求改了,向5G数据的txt文本里追加了,结果XXX君傻了,他内存只有4G,如果强制读取所有的数据并追加,会报内存溢出的异常。
其实上面的需求很简单,如果我们使用JAVA IO体系中的RandomAccessFile类来完成的话,可以实现零内存追加。其实这就是支持任意位置读写类的强大之处。
在这之前,还是先啰嗦的介绍下RandomAccessFile这个类,RandomAccessFile是Java中输入,输出流体系中功能最丰富的文件内容访问类,它提供很多方法来操作文件,包括读写支持,与普通的IO流相比,它最大的特别之处就是支持任意访问的方式,程序可以直接跳到任意地方来读写数据。
如果我们只希望访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将会带来更简洁的代码以及更好的性能。
下面来看下RandomAccessFile类中比较重要的2个方法,其他的和普通IO类似,在这里,就不详细说明了。
public
static
void randomRed(String path,
int pointe){
RandomAccessFile raf=
new RandomAccessFile(path,
"r");
System.out.println(
"RandomAccessFile文件指针的初始位置:"+raf.getFilePointer());
raf.seek(pointe);
byte[] buff=
new
byte[
1024];
int hasRead=
0;
while((hasRead=raf.read(buff))>
0){
System.out.println(
new String(buff,
0,hasRead));
}
catch(Exception e){
e.printStackTrace();
public
static
void main(String[] args) {
String path=
"D:\\3\\test.txt";
int seekPointer=
20;
randomRed(path,seekPointer);
RandomAccessFile raf=
new RandomAccessFile(path,
"rw");
raf.seek(raf.length());
raf.write(
"我是追加的 \r\n".getBytes());
}
catch(Exception e){
e.printStackTrace();
public
static
void main(String[] args) {
String path=
"D:\\3\\test.txt";
randomWrite(path);
public
static
void insert(String fileName,
long points,String insertContent){
File tmp=File.createTempFile(
"tmp",
null);
tmp.deleteOnExit();
RandomAccessFile raf=
new RandomAccessFile(fileName,
"rw");
FileOutputStream tmpOut=
new FileOutputStream(tmp);
FileInputStream tmpIn=
new FileInputStream(tmp);
raf.seek(points);
byte [] buff=
new
byte[
1024];
int hasRead=
0;
while((hasRead=raf.read(buff))>
0){
tmpOut.write(buff,
0, hasRead);
raf.seek(points);
raf.write(insertContent.getBytes());
while((hasRead=tmpIn.read(buff))>
0){
raf.write(buff,
0,hasRead);
}
catch(Exception e){
e.printStackTrace();
public
static
void main(String[] args) {
String path=
"D:\\3\\test.txt";
insert(path,
33,
"\nlucene是一个优秀的全文检索库");
运行效果:
至此,RandomAccessFile类的几个功能,散仙在代码中已给出实现了,现在回到本文开始前的提的那个需求,用RandomAccessFile类就可以轻而易举的完成了,另外需要注意的是,向指定位置插入数据,是散仙自己改造的功能,RandomAccessFile并不直接支持,需要新建一个缓冲区临时空间,存数据,然后在写,因为一旦数据量上了级别,在任意位置插入数据,是很耗内存的,这个也就是为什么hadoop的HDFS文件系统,只支持append的方式,而没有提供修改的操作。
另外我们可以用RandomAccessFile这个类,来实现一个多线程断点下载的功能,用过下载工具的朋友们都知道,下载前都会建立两个临时文件,一个是与被下载文件大小相同的空文件,另一个是记录文件指针的位置文件,每次暂停的时候,都会保存上一次的指针,然后断点下载的时候,会继续从上一次的地方下载,从而实现断点下载或上传的功能,有兴趣的朋友们可以自己实现下。