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

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

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

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

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

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

下面散仙给出示例,分析下怎么使用RandomAccessFile
首先,我们先看下要操作的文本文件的内容截图。


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

  1. /**
  2. * 读的方法
  3. * @param path 文件路径
  4. * @param pointe 指针位置
  5. * **/
  6. public static void randomRed(String path,int pointe){
  7. try{
  8. //RandomAccessFile raf=new RandomAccessFile(new File("D:\\3\\test.txt"), "r");
  9. /**
  10. * model各个参数详解
  11. * r 代表以只读方式打开指定文件
  12. * rw 以读写方式打开指定文件
  13. * rws 读写方式打开,并对内容或元数据都同步写入底层存储设备
  14. * rwd 读写方式打开,对文件内容的更新同步更新至底层存储设备
  15. *
  16. * **/
  17. RandomAccessFile raf=new RandomAccessFile(path, "r");
  18. //获取RandomAccessFile对象文件指针的位置,初始位置是0
  19. System.out.println("RandomAccessFile文件指针的初始位置:"+raf.getFilePointer());
  20. raf.seek(pointe);//移动文件指针位置
  21. byte[]  buff=new byte[1024];
  22. //用于保存实际读取的字节数
  23. int hasRead=0;
  24. //循环读取
  25. while((hasRead=raf.read(buff))>0){
  26. //打印读取的内容,并将字节转为字符串输入
  27. System.out.println(new String(buff,0,hasRead));
  28. }
  29. }catch(Exception e){
  30. e.printStackTrace();
  31. }
  32. }

测试代码

  1. public static void main(String[] args) {
  2. String path="D:\\3\\test.txt";
  3. int seekPointer=20;
  4. randomRed(path,seekPointer);//读取的方法
  5. //randomWrite(path);//追加写的方法
  6. //insert(path, 33, "\nlucene是一个优秀的全文检索库");
  7. }

运行效果:

  1. RandomAccessFile文件指针的初始位置:0
  2. is a teacher
  3. hadoop is perfect

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

  1. /**
  2. * 追加方式
  3. * 写的方法
  4. * @param path 文件路径
  5. * ***/
  6. public static void randomWrite(String path){
  7. try{
  8. /**以读写的方式建立一个RandomAccessFile对象**/
  9. RandomAccessFile raf=new RandomAccessFile(path, "rw");
  10. //将记录指针移动到文件最后
  11. raf.seek(raf.length());
  12. raf.write("我是追加的 \r\n".getBytes());
  13. }catch(Exception e){
  14. e.printStackTrace();
  15. }
  16. }

测试代码

  1. public static void main(String[] args) {
  2. String path="D:\\3\\test.txt";
  3. //int seekPointer=20;
  4. // randomRed(path,seekPointer);//读取的方法
  5. randomWrite(path);//追加写的方法
  6. //insert(path, 33, "\nlucene是一个优秀的全文检索库");
  7. }

运行效果:


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

  1. /**
  2. * 实现向指定位置
  3. * 插入数据
  4. * @param fileName 文件名
  5. * @param points 指针位置
  6. * @param insertContent 插入内容
  7. * **/
  8. public static void insert(String fileName,long points,String insertContent){
  9. try{
  10. File tmp=File.createTempFile("tmp", null);
  11. tmp.deleteOnExit();//在JVM退出时删除
  12. RandomAccessFile raf=new RandomAccessFile(fileName, "rw");
  13. //创建一个临时文件夹来保存插入点后的数据
  14. FileOutputStream tmpOut=new FileOutputStream(tmp);
  15. FileInputStream tmpIn=new FileInputStream(tmp);
  16. raf.seek(points);
  17. /**将插入点后的内容读入临时文件夹**/
  18. byte [] buff=new byte[1024];
  19. //用于保存临时读取的字节数
  20. int hasRead=0;
  21. //循环读取插入点后的内容
  22. while((hasRead=raf.read(buff))>0){
  23. // 将读取的数据写入临时文件中
  24. tmpOut.write(buff, 0, hasRead);
  25. }
  26. //插入需要指定添加的数据
  27. raf.seek(points);//返回原来的插入处
  28. //追加需要追加的内容
  29. raf.write(insertContent.getBytes());
  30. //最后追加临时文件中的内容
  31. while((hasRead=tmpIn.read(buff))>0){
  32. raf.write(buff,0,hasRead);
  33. }
  34. }catch(Exception e){
  35. e.printStackTrace();
  36. }
  37. }

测试代码

  1. public static void main(String[] args) {
  2. String path="D:\\3\\test.txt";
  3. //int seekPointer=20;
  4. // randomRed(path,seekPointer);//读取的方法
  5. // randomWrite(path);//追加写的方法
  6. insert(path, 33, "\nlucene是一个优秀的全文检索库");
  7. }

运行效果:

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

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

Java 实现文件随机读写-RandomAccessFile的更多相关文章

  1. 从零开始学C++之IO流类库(三):文件的读写、二进制文件的读写、文件随机读写

    一.文件的读写 如前面所提,流的读写主要有<<, >>, get, put, read, write 等操作,ofstream 继承自ostream, ifstream 继承自 ...

  2. 营销MM让我讲MySQL日志顺序读写及数据文件随机读写原理

    摘要:你知道吗,MySQL在实际工作时候的两种数据读写机制? 本文分享自华为云社区<MySQL日志顺序读写及数据文件随机读写原理>,作者:JavaEdge . MySQL在实际工作时候的两 ...

  3. 文件随机读写专用类——RandomAccessFile

     RandomAccessFile类可以随机读取文件,但是在测试中并不好用;File类可以测试文件存不存在,不存在可以创建文件;FileWriter类可以对文件进行重写或者追加内容;FileReade ...

  4. Java实现文件的读写,复制

    import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStr ...

  5. java字符文件的读写

    1.java文件读写,首先我们需要导入相应的包:java.io.*; 2.代码如下: package Demo1; import java.io.*; public class FileWirteTe ...

  6. Java实现文件的读写

    需求:实现基本的读写 package com.sbx.io; import java.io.File; import java.io.FileReader; import java.io.FileWr ...

  7. 第9.9节 Python文件随机读写定位操作方法seek

    类似于C语言,Python也提供了文件位置定位的操作方法seek. 一. 语法 seek(offset, whence=SEEK_SET) 语法释义: 1)offset :将文件当前操作位置移动偏移量 ...

  8. java 实现文件读写操作

    import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /* JAVA IO 读写操作 20 ...

  9. java中文件的I/O操作

    java中文件的读写操作 (一) (1)java中文件的字节转成字符读操作 FileInputStream fStream = new FileInputStream("test.txt&q ...

随机推荐

  1. Google Analytics Overview - Google Analytics 概述

    该文档讨论了如何开始使用Google Analytics SDK for Android v3. Before you Begin - 在开始之前 在开始实现SDK之前,请确保有下面的东东: 1.  ...

  2. 小型云服务器搭建GitLab遇到的坑

    云服务商:腾讯云,搞活动买的 3年800块钱,和同时一人一台 配置:1C.1G.50G 用三年,挺划算的 项目中以前一直使用SVN作为代码版本控制,秉着程序员做到老学到老的精神,想尝试一下先进的GIT ...

  3. 关于MongoDB最大连接数的查看与修改

    一. MongoDB连接数 在Linux平台下,无论是64位或者32位的MongoDB默认最大连接数都是819,WIN平台不知道,估计也没有人在 WIN平台下使用MongoDB做生产环境 [root@ ...

  4. SpringBoot 获取前端页面参数的集中方式总结

    SpringBoot的一个好处就是通过注解可以轻松获取前端页面的参数,之后尅将参数经过一系列处理传送到后台数据库,前端时间正好用到.大致分为一下几种: 1.指定前端URL请求参数名称与方法名称一致,这 ...

  5. import _mysql----ImportError: DLL load failed: %1 不是有效的 Win32 应用程序。

    背景:安装了mysql,练习sql 操作,提示 ImportError DLL load failed: %1 不是有效的 Win32 应用程序 解决方法: 操作系统win10,64位,查看安装的my ...

  6. hibernate中的java对象有几种状态,其相互关系如何(区别和相互转换)。

    hibernate中的java对象有几种状态,其相互关系如何(区别和相互转换). 解答:在Hibernate中,对象有三种状态:临时状态.持久状态和游离状态. 临时状态:当new一个实体对象后,这个对 ...

  7. 【vijos】1757 逆序对(dp)

    https://vijos.org/p/1757 有时候自己sb真的是不好说... 我竟然想了半天都没想到这个转移. 我是有多傻.... 我们设f[i][j]表示1~i的排列且逆序对恰好是j的方案数. ...

  8. 邮件正文及其附件的发送的C++实现

     这段代码我花了整整一天来编写,假设转载,请注明出处,谢谢!    前面的一篇文章已经讲了怎样发送邮件正文,原理我就不再叙述了.要了解的同学请到这里查看!    http://blog.csdn.ne ...

  9. 关于spotlight for Windows和spotlight for oracle的使用

    http://blog.csdn.net/luowangjun/article/details/4866084 http://konglx.iteye.com/blog/1873805

  10. MathType编辑钢筋符号就是这么简单

    很多的用户在使用MathType公式编辑器的时候,发现它所包含的符号非常的多,几乎你在数学中看到的任何符号都能用MathType编辑出来.它能够满足各个学科对符号的需求,除了常规的数学物理符号之外,也 ...