一、转换流概述
1.1 编码引出的问题
在Eclipse中,使用FileReader读取硬盘中的文本文件。在Windows系统中,由于Eclipse默认采用的都是GBK编码,当读取UTF-8编码格式的文本文件时,就会出现乱码的情况。
当Eclipse默认采用GBK编码的情况下,那么如何读取UTF-8编码的文件呢?答案:转换流。
1.2 转换流概述
1)Reader和Writer最重要的子类是InputStreamReader和OutputStreamWriter类。
2)转换流本质上就是字符和字节之间的桥梁。
3)InputStreamReader是字节流通向字符流,OutputStreamWriter是字符流通向字节流。
4)转换流原理:字节流+编码表。
1.3 转换流原理
- InputStreamReader类包含了一个底层输入流,可以从中读取原始字节,然后再根据指定的编码方式,将这些字节转换为字符并输出。
- OutputStreamWriter从运行的程序中接收字符数据,然后使用指定的编码方式将这些字符转换为字节,再将这些字节写入底层输出流中。
1.4 为什么需要转换流
一般在操作输入或输出内容的时候就需要使用字节流或字符流,但是有些时候需要将字符流变为字节流的形式,或者将字节流变为字符流的形式,所以,就需要另外一组转换流的操作类。
如果以文件操作为例,则在内存中的字符数据需要通过OutputStreamWriter变为字节流才能保存在文件之中,读取的时候需要将读入的字节流通过InputStreamReader变为字符流
1.5 转换流的使用场合
主要有两个场合:
1、一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。
2、源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换流作为桥梁。
二、InputStreamReader类
2.1概述
InputStreamReader是Reader的子类,将输入的字节流变为字符流,即:将一个字节流的输入对象变为字符流的输入对象。
2.2 构造方法
InputStreamReader是字符体系中的成员,它可以实现字节和字符之间的转换,所以在创建转换流的时候需要传入字节流对象进来。指定字符集可以为UTF-8,UTF-16,GBK、Unicode等,我们根据实际情况选用合适的字符集。
2.3 采用指定的编码读取文本文件
【示例】读取UTF-8的文本文件
public class InputStreamReaderDemo { public static void main(String[] args) throws IOException { // 创建与文件关联的字节输入流对象 FileInputStream fis = new FileInputStream("E:\\demo.txt"); // 创建转换流对象,编码格式指定为utf-8 InputStreamReader isr = new InputStreamReader(fis, "utf-8"); // 读取数据 char[] buf = new char[1024]; int len = 0; while((len = isr.read(buf)) != -1) { System.out.println(new String(buf, 0, len)); } // 关闭流:此处只需关闭转换流即可! isr.close(); } } |
注意:在读取指定的编码文件时,一定要指定编码格式来解码,否则会发生乱码现象。
2.4 字节流System.in转为字符流BufferedReader
System.in表示标准输入流,可以等待并获取键盘输入的文本数据。但System.in属于字节流,而获取一行文本readLine()方法只有字符流能够提供,那么就需要把字节流System.in转为字符流BufferedReader,这就涉及到了把字节流向字符流之间的转换。
【示例】从键盘录入的数据存储到文件中
public class InputStreamReaderTest { public static void main(String[] args) throws IOException { // 创建一个字符输出流,明确目标文件 BufferedWriter bw = new BufferedWriter(new FileWriter("E://a.txt")); // 字节流-->字符流 InputStreamReader isr = new InputStreamReader(System.in); // 创建一个字符输入缓冲流,方便读取键盘输入的一行数据 BufferedReader br = new BufferedReader(isr); // 获取键盘输入的数据 String line = null; // 当输入的字符串为“over”时,结束循环 while(!(line = br.readLine()).equals("over")) { // 将获取的字符串存储 bw.write(line); // 添加换行 bw.newLine(); // 刷新数据 bw.flush(); } // 关闭流 br.close(); bw.close(); } } |
补充:因为使用流接收键盘的输入太复杂了,所以高版本中提供了Scanner类来实现。
三、OutputStreamWriter类
3.1 概述
OutputStreamWriter是Writer的子类,将输出的字符流变为字节流,即:将一个字符流的输出对象变为字节流的输出对象。
3.2 构造方法
OutputStreamWriter是字符体系中的成员,它可以实现字节和字符之间的转换,所以在创建转换流的时候需要传入字节流对象进来。指定字符集可以为UTF-8,UTF-16,GBK、Unicode等,我们根据实际情况选用合适的字符集。
3.3 采用指定的编码输出文本文件
【示例】把文本按照UTF-8格式存储
public class OutputStreamWriterDemo { public static void main(String[] args) throws IOException { // 创建与文件关联的字节输出流对象 FileOutputStream fos = new FileOutputStream("E:\\demo.txt"); // 创建可以把字符转成字节的转换流对象,并指定编码 OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8"); // 写数据 osw.write("武汉动力节点"); // 关闭流:此处只需关闭转换流即可! osw.close(); } } |
原理:
其实在OutputStreamWriter 流中维护自己的缓冲区,当我们调用 OutputStreamWriter 对象的 write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter 缓冲区中。然后再调用刷新功能,或者关闭流,或者缓冲区存满后会把缓冲区中的字节数据使用字节流写到指定的文件中。
3.4 字符流BufferedReader转为字节流System.out
System.out表示标准输出流,可以将数据在控制台输出。但是System.out属于字节流,而写入一行字符串write(String line)方法只有字符流能够提供,那么就需要把字符流BufferedReader转为字节流System.out,这就涉及到了把字符流向字节流之间的转换。
【示例】获取文件中的数据,然后再控制台输出
public class OutputStreamWriterTest { public static void main(String[] args) throws IOException { // 创建一个字符输入流,明确目标文件 BufferedReader br = new BufferedReader(new FileReader("E:\\a.txt")); // 字符流-->字节流 OutputStreamWriter osw = new OutputStreamWriter(System.out); // 创建一个字符输出缓冲流,方便写入一行字符串文本 BufferedWriter bw = new BufferedWriter(osw); // 通过循环,读取文件中的数据 String line = null; while((line = br.readLine()) != null) { // 写入一行文本,也就是在控制台输出一行文本 bw.write(line); // 添加换行 bw.newLine(); // 刷新数据 bw.flush(); } // 关闭流 bw.close(); br.close(); } } |
补充:使用流输出内容到控制台太复杂了,还是使用System.out.println()简洁一些!
四、FileReader和FileWriter
FileReader是InputStreamReader的子类;FileWriter是OutputStreamWriter的子类。
FileWriter和FileReader作为子类,仅作为操作字符文件的便捷类存在。
如果操作的字符文件使用的是默认编码表,那么可以不用父类,而直接用子类就完成操作了,简化了代码。但是,如果操作的字符文件使用了其它指定编码时,那么就绝对不能用子类,必须使用字符转换流。
【示例】创建默认编码的输入流
// 创建字节输入流对象 FileInputStream fis = new FileInputStream("E:\\demo.txt"); // 采用默认编码集 InputStreamReader isr1 = new InputStreamReader(fis); // 采用指定GBK字符集。 InputStreamReader isr2 = new InputStreamReader(fis, "GBK"); // 采用默认编码集的字符输入流 FileReader fr = new FileReader("demo.txt"); |
这三个输入流的在Windows系统中功能是一样的,但是第三个最为便捷。
注:在Windows系统中,Eclipse默认采用GBK编码,那么FileReader默认采用的也是GBK编码。在Linux系统中,Eclipse默认采用UTF-8编码,那么FileReader默认采用的也是UTF-8编码。