• Java开发笔记(八十五)通过字符流读写文件


    前面介绍了文件的信息获取、管理操作,以及目录下的文件遍历,那么文件内部数据又是怎样读写的呢?这正是本文所要阐述的内容。File工具固然强大,但它并不能直接读写文件,而要借助于其它工具方能开展读写操作。对于写操作来说,需要通过文件写入器FileWriter搭配File工具才行。创建写入器对象的过程很简单,只要在调用FileWriter的构造方法时传递文件对象即可,接着就能调用写入器的下列方法向文件写入数据了。
    write:往文件写入字符串。注意该方法存在多个同名的重载方法。
    append:也是往文件写入字符串。按字面意思,append方法像是往文件末尾追加字符串,然而并非如此,append方法与write方法的写入位置是同样的。二者的区别在于,append方法会把空指针当作“null”写入文件,而write方法不支持写入空指针。
    close:关闭文件写入器。
    把文件的一系列写入操作串起来,形成以下流程的写文件代码,注意文件写入器的几个方法均需捕捉输入输出异常IOException:

    	private static String mFileName = "D:/test/aac.txt";
    	// 存在隐患的写文件代码。发生异常时不会关闭文件
    	private static void writeFileSimple() {
    		String str = "白日依山尽,黄河入海流。
    ";
    		File file = new File(mFileName); // 创建一个指定路径的文件对象
    		try {
    			FileWriter writer = new FileWriter(file); // 创建一个文件写入器
    			writer.write(str); // 往文件写入字符串
    			writer.close(); // 关闭文件
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    

    上面的例子代码看似结构完整,实则存在不小的隐患。因为close方法只有在正常分支才会被调用,异常分支并没有调用该方法,如此一来,一旦异常发生,已经打开的文件将不会正常关闭,结果可能导致文件损坏。解决办法是在try/catch后面补充finally语句,在finally语句块中添加close方法的调用,于是改进后的写文件代码示例如下:

    	// 改进后的写文件代码。在finally代码块中关闭文件
    	private static void writeFileWithFinally() {
    		String str = "白日依山尽,黄河入海流。
    ";
    		File file = new File(mFileName); // 创建一个指定路径的文件对象
    		FileWriter writer = null;
    		try {
    			writer = new FileWriter(file); // 创建一个文件写入器
    			writer.write(str); // 往文件写入字符串
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally { // 无论是否遇到异常,都要释放文件资源
    			if (writer != null) {
    				try {
    					writer.close(); // 关闭文件
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    

    改进后的代码确实消除了文件异常关闭的风险,可是整个代码一下子多出好些行,着实变得拖沓不小。为此从Java7开始,try语句支持“try-with-resources”的表达式,意思是携带某些资源去尝试干活,并在尝试结束后自动释放这些资源。具体做法是在try后边添加圆括号,并在圆括号内部填写资源对象的创建语句,只要这个资源类实现了AutoCloseable接口,程序便会在try/catch结束后自动调用该资源的close方法。这样就无需补充finally代码块,也无需显式调用close方法了,采取资源自动管理的优化代码如下所示:

    	// 采取自动释放资源的写文件代码
    	private static void writeFileWithTry() {
    		String str = "白日依山尽,黄河入海流。
    ";
    		File file = new File(mFileName); // 创建一个指定路径的文件对象
    		// Java7的新增功能,在try(...)里声明的资源,会在try/catch结束后自动释放。
    		// 相当于编译器自动补充了finally代码块中的资源释放操作。
    		// 资源类必须实现java.lang.AutoCloseable接口,这样close方法才会由系统调用。
    		// 一般说来,文件I/O、套接字、数据库连接等均已实现该接口。
    		try (FileWriter writer = new FileWriter(file)) {
    			writer.write(str); // 往文件写入字符串
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    

    由此可见,使用“try-with-resources”方式的代码顿时减少到了寥寥几行,可谓程序员的一大福音。

    和写操作对应的是读操作,读文件用到了文件读取器FileReader,它依然与File工具搭档合作。创建读取器对象也要在调用FileReader的构造方法时传递文件对象,读取器提供的调用方法列举如下:
    skip:跳过若干字符。注意FileReader的skip方法跳过的是字符数,不是字节数。
    read:从文件读取数据到字节数组。注意该方法存在多个同名的重载方法。
    close:关闭文件读取器。
    通过文件读取器从文件中读取数据的常规代码示例如下:

    	// 存在隐患的读文件代码。发生异常时不会关闭文件
    	private static void readFileSimple() {
    		File file = new File(mFileName); // 创建一个指定路径的文件对象
    		try {
    			FileReader reader = new FileReader(file); // 创建一个文件读取器
    			//reader.skip(2); // 字符流的skip方法跳过的是字符数,不是字节数
    			char[] temp = new char[(int) file.length()];
    			reader.read(temp); // 从文件读取数据到字节数组
    			String content = new String(temp); // 把字符数组转为字符串
    			System.out.println("content="+content);
    			reader.close(); // 关闭文件
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    

    以上的读文件代码仍然没有考虑到异常发生时候的资源释放问题,因而需要增加finally语句加以改进,在finally代码块中调用close方法关闭文件,改进后的代码如下所示:

    	// 改进后的读文件代码
    	private static void readFileWithFinally() {
    		File file = new File(mFileName); // 创建一个指定路径的文件对象
    		FileReader reader = null;
    		try {
    			reader = new FileReader(file); // 创建一个文件读取器
    			char[] temp = new char[(int) file.length()];
    			reader.read(temp); // 从文件读取数据到字节数组
    			String content = new String(temp); // 把字符数组转为字符串
    			System.out.println("content="+content);
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally { // 无论是否遇到异常,都要释放文件资源
    			if (reader != null) {
    				try {
    					reader.close(); // 关闭文件
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    

    同FileWriter一样,FileReader也实现了AutoCloseable接口,意味着它同样适用于try-with-resources的规则。那么将文件读取器的创建语句放到try之后的圆括号中,之前的finally语句块可以整个删除,因为程序会在try/catch结束后自动释放读取器资源。此时采取自动释放资源的读文件代码变成了下面这样:

    	// 采取自动释放资源的读文件代码
    	private static void readFileWithTry() {
    		File file = new File(mFileName); // 创建一个指定路径的文件对象
    		// Java7的新增功能,在try(...)里声明的资源,会在try/catch结束后自动释放。
    		// 相当于编译器自动补充了finally代码块中的资源释放操作。
    		// 资源类必须实现java.lang.AutoCloseable接口,这样close方法才会由系统调用。
    		// 一般说来,文件I/O、套接字、数据库连接等均已实现该接口。
    		try (FileReader reader = new FileReader(file)) {
    			char[] temp = new char[(int) file.length()];
    			reader.read(temp); // 从文件读取数据到字节数组
    			String content = new String(temp); // 把字符数组转为字符串
    			System.out.println("content="+content);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    

    别看上面的代码还有好几行,要是全部去掉注释真没多少行。省时省力的便捷写法理应大力推广,若无特殊情况,往后的相关代码将一律采用以上try-with-resources的写法。



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

  • 相关阅读:
    [NOIP2013提高组] CODEVS 3287 火车运输(MST+LCA)
    BZOJ 2763: [JLOI2011]飞行路线(最短路)
    BZOJ 1083: [SCOI2005]繁忙的都市(MST)
    USACO Seciton 5.4 Canada Tour(dp)
    HDOJ 3415 Max Sum of Max-K-sub-sequence(单调队列)
    POJ2823 Sliding Window(单调队列)
    USACO Section 5.4 TeleCowmunication(最小割)
    [IOI1996] USACO Section 5.3 Network of Schools(强连通分量)
    USACO Section 5.3 Milk Measuring (IDDFS+dp)
    USACO Section 5.3 Big Barn(dp)
  • 原文地址:https://www.cnblogs.com/pinlantu/p/10712046.html
Copyright © 2020-2023  润新知