现有如下的一个需求,向已存在1G数据的txt文本里末尾追加一行文字,内容如下“Lucene是一款非常优秀的全文检索库”。可能大多数朋友会觉得这个需求很easy,说实话,确实easy,然后XXX君开始实现了,直接使用Java中的流读取了txt文本里原来所有的数据转成字符串后,然后拼接了“Lucene是一款非常优秀的全文检索库”,又写回文本里了,至此,大功告成。后来需求改了,向5G数据的txt文本里追加了,结果XXX君傻了,他内存只有4G,如果强制读取所有的数据并追加,会报内存溢出的异常。

其实上面的需求很简单,如果我们使用JAVA IO体系中的RandomAccessFile类来完成的话,可以实现零内存追加。其实这就是支持任意位置读写类的强大之处。

RandomAccessFile是Java中输入,输出流体系中功能最丰富的文件内容访问类,它提供很多方法来操作文件,包括读写支持,与普通的IO流相比,它最大的特别之处就是支持任意访问的方式,程序可以直接跳到任意地方来读写数据。

如果我们只希望访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将会带来更简洁的代码以及更好的性能。

下面来看下RandomAccessFile类中比较重要的2个方法,其他的和普通IO类似,在这里,就不详细说明了。

方法名 作用
getFilePointer() 返回文件记录指针的当前位置
seek(long pos) 将文件记录指针定位到pos的位置

功能one,读取任意位置的数据,代码如下|

/**
* 读的方法
* @param path 文件路径
* @param pointe 指针位置
* **/
public static void randomRed(String path,int pointe){
try{
//RandomAccessFile raf=new RandomAccessFile(new File("D:\\3\\test.txt"), "r");
/**
* model各个参数详解
* r 代表以只读方式打开指定文件
* rw 以读写方式打开指定文件
* rws 读写方式打开,并对内容或元数据都同步写入底层存储设备
* rwd 读写方式打开,对文件内容的更新同步更新至底层存储设备
*
* **/
RandomAccessFile raf=new RandomAccessFile(path, "r");
//获取RandomAccessFile对象文件指针的位置,初始位置是0
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);//读取的方法
//randomWrite(path);//追加写的方法
//insert(path, 33, "\nlucene是一个优秀的全文检索库");
}
RandomAccessFile文件指针的初始位置:0
is a teacher
hadoop is perfect

功能two,追加数据,代码如下

/**
* 追加方式
* 写的方法
* @param path 文件路径
* ***/
public static void randomWrite(String path){
try{
/**以读写的方式建立一个RandomAccessFile对象**/
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";
//int seekPointer=20;
// randomRed(path,seekPointer);//读取的方法
randomWrite(path);//追加写的方法
//insert(path, 33, "\nlucene是一个优秀的全文检索库");
}

功能three,任意位置插入数据,代码如下

/**
* 实现向指定位置
* 插入数据
* @param fileName 文件名
* @param points 指针位置
* @param insertContent 插入内容
* **/
public static void insert(String fileName,long points,String insertContent){
try{
File tmp=File.createTempFile("tmp", null);
tmp.deleteOnExit();//在JVM退出时删除 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";
//int seekPointer=20;
// randomRed(path,seekPointer);//读取的方法
// randomWrite(path);//追加写的方法
insert(path, 33, "\nlucene是一个优秀的全文检索库");
}

至此,RandomAccessFile类的几个功能,散仙在代码中已给出实现了,现在回到本文开始前的提的那个需求,用RandomAccessFile类就可以轻而易举的完成了,另外需要注意的是,向指定位置插入数据,是散仙自己改造的功能,RandomAccessFile并不直接支持,需要新建一个缓冲区临时空间,存数据,然后在写,因为一旦数据量上了级别,在任意位置插入数据,是很耗内存的,这个也就是为什么hadoop的HDFS文件系统,只支持append的方式,而没有提供修改的操作。

另外我们可以用RandomAccessFile这个类,来实现一个多线程断点下载的功能,用过下载工具的朋友们都知道,下载前都会建立两个临时文件,一个是与被下载文件大小相同的空文件,另一个是记录文件指针的位置文件,每次暂停的时候,都会保存上一次的指针,然后断点下载的时候,会继续从上一次的地方下载,从而实现断点下载或上传的功能,有兴趣的朋友们可以自己实现下。

http://blog.csdn.net/czplplp_900725/article/details/37809579

最新文章

  1. WEB安全:XSS漏洞与SQL注入漏洞介绍及解决方案(转)
  2. Husky or C++ API - HDFS Short-Circuit Local Reads
  3. 【leetcode】Insert Interval(hard)★
  4. CF 22B. Bargaining Table
  5. CSS3 filter:drop-shadow滤镜与box-shadow区别应用 抄的
  6. docker容器服务器 - centos atomic host
  7. import com.sun.image.codec.jpeg.JPEGCodec不通过 Eclipse找不到包
  8. Java得到的一周的最后一天的一段时间内
  9. Linux安装配置Mariadb
  10. js如何使用正则表达式实现过滤HTML标签?(/<[^<>]+>/g)
  11. SVN简单使用
  12. Dev修改gridview 背景色
  13. Codeforces 837D Round Subset - 动态规划 - 数论
  14. session和cokkie的区别与作用
  15. 学习ES6的全部特性
  16. Oracle 11gR2 RAC 常用维护操作 说明
  17. C++ 获取类成员函数地址方法 浅析
  18. .net 微信支付(公众号支付)遇到的问题
  19. 有关查询和执行计划的DMV
  20. 中兴G718C开发者模式开启

热门文章

  1. Web监听器导图详解(转)
  2. Java Executor 框架
  3. 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁
  4. NET Core控制反转(IoC)
  5. 上门洗车APP --- Androidclient开发 之 项目结构介绍
  6. JavaEEB2C网上商城前端系统
  7. 王立平--Unity综上所述控制
  8. [Java] HttpClient有个古怪的stalecheck选项
  9. 索尼 LT26I刷机包 X.I.D 增加官方风格 GF A3.9.4 各方面完美
  10. Thinkphp编辑器扩展类kindeditor用法