字符流是什么
字符流是可以直接读写字符的IO流
字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
在前面简单介绍了IO和学习了字节流(点此传送门),学习完字节流后你会发现基本上字符流可以自学了,因为许多用法都非常相似。
两图看完字符流
(注:图片来源于:http://ankonlili.iteye.com)
其中深色的为节点流,浅色的为处理流。在此简单介绍下两种区别
按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。
节点流:可以从或向一个特定的地方(节点)读写数据。如FileReader.
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader.
字符流 FileReader
FileReader类的read()方法可以按照字符大小读取
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("read.txt");
int ch;
//根据项目默认的码表一次读取一个字符
while((ch = fr.read()) != -1) {
System.out.println((char)ch);
}
fr.close();
}
字符流 FileWriter
FileWriter类的write()方法可以自动把字符转为字节写出
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("write.txt");
fw.write("world你好");
fw.close();
}
万一你忘记关闭流了这么办?结果会一样吗?(将fw.close() 注释)
你会发现,什么都没有,在讲BufferedOutputStream时说过,该close方法带自动刷新缓冲区功能,难道FileWriter里也有缓冲区?
答案是明显的,该缓冲区是在Writer里的(继承下来),查看该源码
public abstract class Writer implements Appendable, Closeable, Flushable {
private char[] writeBuffer;//缓冲区数组
private static final int WRITE_BUFFER_SIZE = 1024;//缓冲区大小
//相当于容量为2k的缓冲区
}
字符流的拷贝
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("read.txt");
FileWriter fw = new FileWriter("write.txt");
int ch;
while((ch = fr.read()) != -1) {
fw.write(ch);
}
fr.close();
fw.close();
}
你会发现向上面的这样单纯拷贝文本文件,是不用字符流拷贝也是可以的(即便里面有中文字符)。为什么?
首先来讲讲码表,计算机内保存在内存、硬盘的数据都是二进制的,为什么我们能看到字符,就是码表的功能。
码表其实就是一个字符和其对应的二进制相互映射的一张表。这张表中规定了字符和二进制的映射关系。计算机存储字符时将字符查询码表,然后存储对应的二进制计算机;取出字符时将二进制查询码表,然后转换成对应的字符显示。
当使用字节流时,他会一个字节一个字节不赖的copy过去,最后根据其码表显示。而字符的拷贝还是要先将字节形式读取,在根据码表转换为字符,最后再根据码表转换为字节写入,最后根据其码表显示。这样看来,使用字符的copy是不是挺多余的。
字符流拷贝简单原理图:
假设使用GBK码表(中文占两个字节,第一个字节为负数,第二个字节为正数和负数),由于中文第一个字节肯定是负数,在读取的时候,若发现第一个字节为负数,则一起读两个字节,这样就通过码表读取了一个中文字符了