InputStreamReader和OutputStreamWriter
由于字节流操作中文不是特别方便,所以,Java就提供了转换流。为了能更好地操作中文数据,我们就需要学习一下字符流(字符流 = 字节流 + 编码表)了,这样就需要学习InputStreamReader和OutputStreamWriter这两个类了。
编码表
什么是编码表
由于计算机只能识别二进制数据,为了方便应用计算机,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。
常见的编码表
编码与解码
编码:
把看得懂的变成看不懂的。
即将字符串变成字节数组(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);
}
}
运行结果
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();
}
}
OutputStreamWriter(字符流---->字节流)
OutputStreamWriter是字符流通向字节流的桥梁,可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
构造器
OutputStreamWriter写数据的方式
每次调用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来解码,否则会出现乱码。
- 先用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();
}
}
- 先用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);
}
}
出现乱码
原因分析:
由于采用不同的编码集写出数据和读取数据,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 ---ソ
*/
}
}
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();
}
}
复制文件
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();
}
}