Java IO的RandomAccessFile的使用(1)

文章来源:http://blog.csdn.net/czplplp_900725/article/details/37809579

现有如下的一个需求,向已存在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的位置


import java.io.*;

public class RandomAccessFileTest {

	/**
	 * 读的方法
	 * 
	 * @param path
	 *            文件路径
	 * @param pointe
	 *            指针位置
	 **/
	public static void randomRead(String path, int pointe) {
		try {
			/**
			 * 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();
		}
	}

	/**
	 * 追加方式 写的方法
	 * 
	 * @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();
		}

	}

	/**
	 * 实现向指定位置 插入数据
	 * 
	 * @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://test.txt";
		int seekPointer = 20;
		// randomRead(path,seekPointer);//读取的方法
		// randomWrite(path);//追加写的方法
		insert(path, 33, "\nlucene是一个优秀的全文检索库");
	}
}


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

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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值