zoukankan      html  css  js  c++  java
  • Java开发笔记(八十七)随机访问文件的读写

    前面介绍了字符流读写文件的两种方式,包括文件字符流和缓存字符流,但是它们的写操作都存在一个问题:不管是write方法还是append方法,都只能从文件开头写入,而不能追加到文件末尾或者在文件中间某个位置写入。这个问题真不好办,它意味着每次写操作都会覆盖掉原来的文件内容,注意是直接覆盖而非局部修改,可大多数的业务场景需要在原文件基础上追加或者修改的。倘若坚持使用字符流修改文件内容,也不是不可以,那样得把原来的文件内容全部读到某个字符串,再对该字符串进行修改操作,最后把改后的字符串重新写入原文件。这么处理的话,对付小文件倒还凑合,要是遇到超大文件,比如大小达到1G的文件,光光把这1G的数据读到内存就足以让程序崩溃了。因此,通过字符流修改文件并非好办法,不如采用专门的文件修改工具即RandomAccessFile(随机访问文件类),该工具特别适合对文件做各种花式修改。随机文件工具RandomAccessFile提供了seek方法用来定位当前的读写位置,可以很方便地在指定位置写入数据,故而RandomAccessFile经常用于以下几个场合:
    1、往大文件末尾追加数据。
    2、下载文件时候的断点续传,支持从上次已下载完成的地方中途开始,而不必重头下载整个文件。
    创建随机文件对象依然要指定文件路径,同时还要指定该文件的打开方式,下面是创建随机文件对象的代码例子:

    		// 根据文件路径创建既可读又可写的随机文件对象
    		String mAppendFileName = "D:/test/random_appendStr.txt";
    		RandomAccessFile raf = new RandomAccessFile(mAppendFileName, "rw");
    

    上面构造方法的第二个参数值为rw,表示以既可读又可写的模式打开文件。除了常见的rw,模式参数还有其它取值,具体的取值说明如下:
    r:以只读方式打开指定文件。如果试图对该文件执行write写入方法,则会抛出异常IOException。
    rw:以可读且可写的方式打开指定文件。如果该文件不存在,则尝试创建新文件。
    rws:以可读且可写的方式打开指定文件。rws模式的每次write方法都会立即写入文件,它相当于FileWriter;而rw模式先把数据写到缓存,等到缓存满了或者调用close方法关闭文件之时,才将缓存中的数据真正写入文件,它相当于BufferedWriter。
    rwd:与rws模式类似。区别在于rwd只更新文件内容,不更新文件的元数据,而rws模式会同时更新文件内容及元数据。所谓元数据保存了文件的基本信息,包括文件类型(是文件还是目录)、文件的创建时间、文件的修改时间、文件的访问权限(是否可读、是否可写、是否可执行)等等。
    与字符流工具相比,随机文件工具用起来反而更简单,一个RandomAccessFile就集成了File、FileWriter、FileReader三个工具的基本用法,它的主要方法说明如下:
    length:获取指定文件的文件大小。
    setLength:设置指定文件的文件大小。
    seek:移动指定文件的访问位置。
    write:往文件的当前位置写入字节数组。
    read:把当前位置之后的文件内容读到字节数组。
    close:关闭文件。RandomAccessFile拥有close方法,意味着它支持try-with-resources方式的自动释放资源。
    以在文件末尾追加数据为例,使用RandomAccessFile完成的话,先调用seek方法定位到文件末尾,再调用write方法写入字节数组形式的数据。这个追加功能的实现代码如下所示:

    	private static String mAppendFileName = "D:/test/random_appendStr.txt";
    	// 往随机文件末尾追加字符串
    	private static void appendStr() {
    		// 创建指定路径的随机文件对象(可读写)。try(...)支持在处理完毕后自动关闭随机文件
    		try (RandomAccessFile raf = new RandomAccessFile(mAppendFileName, "rw")) {
    			long length = raf.length(); // 获取随机文件的长度(文件大小)
    			raf.seek(length); // 定位到指定长度的位置
    			String str = String.format("你好世界%.10f
    ", Math.random());
    			raf.write(str.getBytes()); // 往随机文件写入字节数组
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    

    从上面代码看到,随机文件工具能够直接往文件末尾添加数据,即使原文件有好几个G大小,也丝毫不影响数据追加的效率。
    再看一个往文件内部的任意位置插入数据的例子,仍然是先调用seek方法跳到指定位置,再调用write方法写入字节数据。下面的演示代码中,为了确保seek跳转的位置始终落在文件内部,在一开始就调用setLength方法设置文件的固定大小。在任意位置插入数据的详细代码参见如下:

    	private static String mFixsizeFileName = "D:/test/random_fixsize.txt";
    	// 往固定大小的随机文件中插入数据
    	private static void fixSizeInsert() {
    		// 创建指定路径的随机文件对象(可读写)。try(...)支持在处理完毕后自动关闭随机文件
    		try (RandomAccessFile raf = new RandomAccessFile(mFixsizeFileName, "rw")) {
    			raf.setLength(1000); // 设置随机文件的长度(文件大小)
    			for (int i=0; i<=2 ;i++) {
    				raf.seek(i*200); // 定位到指定长度的位置
    				String str = String.format("你好世界%.10f
    ", Math.random());
    				raf.write(str.getBytes()); // 往随机文件写入字节数组
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    

    最后瞧瞧随机文件工具的读文件操作,与字符流工具比较,它俩的处理流程大体一致,但在细节上有个区别:随机文件工具的read方法支持一次性读到字节数组,而字符流工具的read方法支持一次性读到字符数组。下面是通过RandomAccessFile读取文件内容的代码例子,可以看到它是以字节为单位读出数据的:

    	// 读取随机文件的文件内容
    	private static void readContent() {
    		// 创建指定路径的随机文件对象(只读)。try(...)支持在处理完毕后自动关闭随机文件
    		try (RandomAccessFile raf = new RandomAccessFile(mAppendFileName, "r")) {
    			int length = (int) raf.length(); // 获取随机文件的长度(文件大小)
    			byte[] bytes = new byte[length]; // 分配长度为文件大小的字节数组
    			raf.read(bytes); // 把随机文件的文件内容读取到字节数组
    			String content = new String(bytes); // 把字节数组转成字符串
    			System.out.println("content="+content);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    

      


    更多Java技术文章参见《Java开发笔记(序)章节目录

  • 相关阅读:
    day7 面向对象 静态方法 类方法 属性方法 类的特殊成员方法 元类 反射 异常处理
    day6 面向对象 封装 继承 多态 类与实例在内存中的关系 经典类和新式类
    day5 time datetime random os sys shutil json pickle shelve xml configparser hashlib subprocess logging re正则 python计算器
    kafka常用操作命令
    linux基础
    django学习1——初识web应用程序
    mysql数据库(三)——pymysql模块
    mysql数据库(二)——表的查询
    mysql数据库(一)
    Python常用模块——re模块
  • 原文地址:https://www.cnblogs.com/pinlantu/p/10740065.html
Copyright © 2011-2022 走看看