InputStreamReader和OutputStreamWriter

由于字节流操作中文不是特别方便,所以,Java就提供了转换流。为了能更好地操作中文数据,我们就需要学习一下字符流(字符流 = 字节流 + 编码表)了,这样就需要学习InputStreamReader和OutputStreamWriter这两个类了。

编码表

什么是编码表
由于计算机只能识别二进制数据,为了方便应用计算机,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。

常见的编码表

Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_System

编码与解码

编码:
  把看得懂的变成看不懂的。
  即将字符串变成字节数组(String→byte[]),
  代表方法为str.getBytes(charsetName);
  
  解码:
  把看不懂的变成看得懂的。
  即将字节数组变成字符串(byte[]→String),
  代表方法为new String(byte[], charsetName)。

注意:
由什么编码表进行编码的,则需要通过对应的编码表进行解码,否则,会出现乱码。
即由UTF-8编码,自然要通过UTF-8来解码;同理,由GBK来编码,自然也要通过GBK来解码。

package demo02;

import java.util.Arrays;

public class Test {

	public static void main(String[] args) throws Exception {
		/*
		 * 通过GBK来编码,通过GBK来解码
		 */
		String s1 = "你好";
		System.out.println(s1);
		byte[] b = s1.getBytes("GBK");//默认编码方式为GBK
		System.out.println(Arrays.toString(b));
		String s2 = new String(b,"GBK");//通过GBK来解码
		System.out.println(s2);	
	}
}

运行结果

Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_System_02

InputStreamReader(字节流---->字符流)

InputStreamReader是字节流通向字符流的桥梁,它使用指定的charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集(GBK)。

将内容为“你好”的text.txt文本文件使用字符流将其读取出来

package demo01;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.util.Arrays;

public class Test1 {
	public static void main(String[] args) throws Exception {
		/*
		 * 新建一个输出流
		 */
		FileOutputStream fos = new FileOutputStream("text.txt");
		//内容
		String s = "你好";
		byte[] b1 = s.getBytes();
		System.out.println(Arrays.toString(b1));
		//将"你好"写入输入流中
		fos.write(b1);
		fos.close();
		
		/*
		 * 使用字符流读取文件
		 */
		//操作字节流的字符流对象,必须先有字节流
		FileInputStream fis = new FileInputStream("text.txt");
		//新建一个字符流,传入字节流对象(建立字节向字符的桥梁)
		InputStreamReader isr = new InputStreamReader(fis);
		/*
		 * 开始读取数据
		 * 因为文本内容为"你好",两个字节,所以读取两次即可
		 */
		int i = isr.read();
		System.out.println((char)i);
		int i1 = isr.read();
		System.out.println((char)i1);
		int i2 = isr.read();
		System.out.println(i2);
		isr.close();
	
	}
}

Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_java_03

OutputStreamWriter(字符流---->字节流)

OutputStreamWriter是字符流通向字节流的桥梁,可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

构造器

Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_System_04


OutputStreamWriter写数据的方式

Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_java_05


每次调用write()方法都会使编码转换器在给定字符上被调用。 所得到的字节在写入底层输出流之前累积在缓冲区中。 可以指定此缓冲区的大小,但是默认情况下它大部分用于大多数目的。 请注意,传递给write()方法的字符不会缓冲。

package demo01;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class Test2 {
	public static void main(String[] args) throws Exception {
		//创建字节流对象
		FileOutputStream fos = new FileOutputStream("text1.txt");
		//创建字符流
		OutputStreamWriter osw = new OutputStreamWriter(fos);
		//写一个字符
		//写入数据时,会存储到缓冲区中,
		osw.write('你');
		osw.write('好');
		osw.write('a');
		osw.write("中国人");
		//刷新缓冲区,将数据写出到目的地中。
		//osw.flush();
		//关闭资源
		osw.close();
		
		/*
		 * 注意:
		 * flush()仅仅刷新缓冲区,刷新之后,流对象还可以继续使用。
		 * close()关闭流对象,但是先刷新一次缓冲区。关闭之后,流对象不可以继续再使用了;
		 */
	}
}

通过给定字符集操作字符

字符流的出现是为了方便操作字符,更重要的是加入了编码转换,通过两个转换流——InputStreamReader和OutputStreamWriter来完成,在两个对象进行构造的时候可以加入字符集。详情可见OutputStreamWriter构造器。
可以将字符以指定编码格式存储,然后对文本数据指定相对应的编码格式来解读,在构造函数时候指定编码表。
注意:
由UTF-8编码,则要通过UTF-8来解码;同理,由GBK来编码,自然也要通过GBK来解码,否则会出现乱码。

  1. 先用UTF-8编码,再通过UTF-8来解码
package demo01;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;

public class Test3 {
	public static void main(String[] args) throws Exception {
		/**
		 * 先用UTF-8编码,再通过UTF-8来解码
		 */
		//新建一个输出流,将数据按照指定编码集写出
		FileOutputStream fos = new FileOutputStream("text.txt");
		OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
		osw.write("你好");
		osw.close();
		
		//创建一个输入流,按照指定的编码集读取数据
		FileInputStream fis = new FileInputStream("text.txt");
		InputStreamReader isr = new InputStreamReader(fis,"utf-8");
		char[] c = new char[1024];
		int len = isr.read(c);
		String s = new String(c, 0, len);
		System.out.println(s);
//		System.out.println((char)isr.read());
//		System.out.println((char)isr.read());
		isr.close();
		
	}
}

Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_字符流_06

  1. 先用UTF-8编码,再通过GBK来解码
package demo01;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Test4 {

	public static void main(String[] args) throws Exception {
		/**
		 * 先用UTF-8编码,再通过GBK来解码
		 */
		FileOutputStream fos = new FileOutputStream("text.txt");
		OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
		osw.write("你好");
		osw.close();
		
		//创建一个输入流,按照指定的编码集读取数据
		FileInputStream fis = new FileInputStream("text.txt");
		InputStreamReader isr = new InputStreamReader(fis,"GBK");
		char[] c = new char[1024];
		isr.read(c);
		System.out.println(c);

	}

}

Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_字符流_07


出现乱码

原因分析:
由于采用不同的编码集写出数据和读取数据,utf-8采用三个字节表示一个字符,而GBK则采用两个字节表示一个字符。因此,在解码的过程中,就出现了问题。

package demo01;
import java.util.Arrays;
public class Test4 {
	public static void main(String[] args) throws Exception {
		
		String s = "你好";
		byte[] b1 = s.getBytes("utf-8");//使用utf-8进行解码
		System.out.println("utf-8:"+Arrays.toString(b1));
		byte[] b2 = s.getBytes("GBK");//使用GBK进行解码
		System.out.println("GBK:"+Arrays.toString(b2));
		//按照默认编码集GBK解码
		System.out.println("GBK解码:"+new String(b1));
		/*
		 * -28,-67  ---浣
		 * -96,-27  ---犲
		 * -91,-67  ---ソ
		 */
	
	}
}

Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_System_08


Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_java_09

FileReader和FileWriter

转换流InputStreamReader和OutputStreamWriter分别有其对应的子类FileReader和FileWriter,是用于操作字符文件的便捷类,但是具有局限性,只能操作字符文件,而且是默认编码

package demo01;

import java.io.FileReader;
import java.io.FileWriter;

public class Test5 {

	public static void main(String[] args) throws Exception {
//		FileOutputStream fos = new FileOutputStream("text.txt");
//		OutputStreamWriter osw = new OutputStreamWriter(fos);
//		等效于下列表达式
		FileWriter fw = new FileWriter("text.txt");
		fw.write("你好");
		fw.close();
		
//		FileInputStream fis = new FileInputStream("text.txt");
//		InputStreamReader isr = new InputStreamReader(fis);
//		等效于下列表达式
		FileReader fr = new FileReader("text.txt");
		int ch = 0;
		while((ch=fr.read())!=-1){
			System.out.println((char)ch);
		}
		fr.close();
	}

}

Java代码怎么实现主码流和辅码流的切换呢 java 流 编码_字符流_10

复制文件

package demo01;

import java.io.FileReader;
import java.io.FileWriter;

public class TestCopy {

	public static void main(String[] args) throws Exception {
		//定义一个字符读取流
		FileReader fr = new FileReader("text.txt");
		//定义一个字符输出流
		FileWriter fw = new FileWriter("text(copy).txt");
		//定义一个缓冲区,用来存储字符信息
		char[] buf = new char[1024];
		int len=0;
		//读取数据
		while((len=fr.read(buf))!=-1){
			//复制数据,将读取到的字符写出
			fw.write(buf, 0, len);
		}
		//关闭流
		fr.close();
		fw.close();
	}

}