J04-Java IO流总结四 《 FileReader和FileWriter 》
filereader和filewriter的源码非常简单,下面通过分析它们的源码以更好地进行理解这两个流
1. filereader
filereader实现了读取底层的字节数据并将其转换为字符数据的功能,转换时依赖的字符集为平台默认的字符集gbk(windows平台)。
filereader源码如下:
public class filereader extends inputstreamreader { public filereader(string filename) throws filenotfoundexception { super(new fileinputstream(filename)); } public filereader(file file) throws filenotfoundexception { super(new fileinputstream(file)); } public filereader(filedescriptor fd) { super(new fileinputstream(fd)); } }
由源码不难看出,filereader类继承自inputstreamreader类,它自己只提供了几个构造方法,它的构造方法中又通过super来调用父类构造器以构建流对象,它本身没有再提供其他的读取流数据的方法,全部继承它的直接父类inputstreamreader,而inputstreamreader又是继承自reader类,因此filereader和inputstreamreader一样,都能使用read()、read(char cbuf[])、read(char cbuf[], int off, int len)方法来读取流数据。
此外,由源码可知,filereader的读取字符串的功能是通过转换流inputstreamreader类实现的:首先inputstreamreader包装了一个fileinputstream从文件读取字节数据,再将其转换为字符,filereader继承了inputstreamreader,从而也获得了该功能。我们截取该类的其中一个构造方法的代码出来:
public filereader(string filename) throws filenotfoundexception { super(new fileinputstream(filename)); }
可以看到,在调用父类inputstreamreader转换流的构造方法时,没有显式地指定解码所需的字符集,因此使用的是平台默认字符集来将读到的字节数据转换为字符。因此,filereader去读取gbk编码的源文件数据时不会出现乱码,而在读取utf-8等其他字符集编码的文件时就会粗现乱码了。
从该流的api文档同样可以看出这点:filereader是用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 fileinputstream 上构造一个 inputstreamreader。 filereader 用于读取字符流。要读取原始字节流,请考虑使用 fileinputstream。
综上,可知filereader的本质其实还是字节流!
filereader类使用示例代码:
import java.io.filenotfoundexception; import java.io.filereader; import java.io.ioexception; public class filereadertest { public static void main(string[] args) { system.out.println("【从gbk文件读取到的内容】:"); test1(); system.out.println("\n\n【从utf-8文件读取到的内容】:"); test2(); } //////////////////////////////////////////////////////////// /** * 使用filereader从编码为gbk的源文件1.txt中读取数据,能正确读取 */ private static void test1() { filereader fr = null; try { fr = new filereader("./src/res/1.txt"); int value = 0; while(-1 != (value = fr.read())) { //使用read()方法读取 system.out.print((char)value); } } catch (filenotfoundexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } finally { if(null != fr) { try { fr.close(); } catch (ioexception e) { e.printstacktrace(); } } } } //////////////////////////////////////////////////////////// /** * 使用filereader从编码为utf-8的源文件2.txt中读取数据,将出现乱码 */ private static void test2() { filereader fr = null; try { fr = new filereader("./src/res/2.txt"); int len = 0; char[] buf = new char[1024]; //注意这里是字符数组!! while(-1 != (len = fr.read(buf))) { //使用read(char cbuf[])方法读取 system.out.println(new string(buf)); } } catch (filenotfoundexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } finally { if(null != fr) { try { fr.close(); } catch (ioexception e) { e.printstacktrace(); } } } } }
代码运行效果:
【从gbk文件读取到的内容】: hello java hello hello 中国 【从utf-8文件读取到的内容】: hello java hello hello 涓浗
可见,若是使用filereader从utf-8文件中读取数据,将会出现乱码,原因我们在上面也说了,是因为filereader的底层转换流inputstreamreader在将字节数据转换为字符数据时,使用的是平台默认的字符集gbk,与源文件的不一致,由此导致了乱码。
2. filewriter
filewriter实现了将字符数据转换为字节数据的功能。它所依赖的字符集也是平台默认的gbk 。
该流的api文档有如下描述:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 fileoutputstream 上构造一个 outputstreamwriter。
filewriter源码如下:
public class filewriter extends outputstreamwriter { public filewriter(string filename) throws ioexception { super(new fileoutputstream(filename)); } public filewriter(string filename, boolean append) throws ioexception { super(new fileoutputstream(filename, append)); } public filewriter(file file) throws ioexception { super(new fileoutputstream(file)); } public filewriter(file file, boolean append) throws ioexception { super(new fileoutputstream(file, append)); } public filewriter(filedescriptor fd) { super(new fileoutputstream(fd)); } }
由filewriter的源码可以看到,该类的直接父类是转换流outputstreamwriter,它跟filereader简直一模一样——自己只提供了几个构造方法,并在它的构造方法中又通过super来调用父类构造器以构建流对象,它本身没有再提供其他的写出流数据的方法,全部继承它的直接父类outputstreamwriter,而outputstreamwriter又是继承自writer类,因此filewriter和outputstreamwriter一样,都能使用write(int c)、write(char cbuf[])、write(char cbuf[], int off, int len)、write(string str)、write(string str, int off, int len)方法来写入流数据。
filewriter流使用示例代码:
import java.io.filewriter; import java.io.ioexception; public class filewritertest { public static void main(string[] args) { filewriter fw = null; try { fw = new filewriter("./src/res/3.txt"); string str1 = "hello java"; string str2 = "中国"; char[] array = str1.tochararray();//将字符串转换为字符数组 fw.write(str1, 0, 5); //写入:hello fw.write(str2); //写入:中国 fw.write(array); //写入:hello java fw.write(array, 6, 4); //写入:java } catch (ioexception e) { e.printstacktrace(); } finally { if(null != fw) { try { fw.close(); } catch (ioexception e) { e.printstacktrace(); } } } } }
代码运行效果:
本次示例中,是将数据写入gbk编码的目标文件中,倘若目标是utf-8或者其他编码,则写入的数据中文会发生乱码,如下所示:
使用filereader和filewriter在两个文件之间相互拷贝文件的操作跟前边的示例差不多,这里不再写了。
3. 小结
fileinputstream、fileoutputstream、filereader和filewriter都是跟磁盘文件有关的字节流,但凡需要跟磁盘文件进行i/o操作的,必将使用到它们中的其中一个!需要根据读取的是字节流还是字符流来进行相应的选择。