天天看点

IO流复习总结(三) 字符流、编码表

字符流

我们字节流读字符数据,需要人工去做字节和字符数据的转换。我们自己转换,我们不清楚哪些字节应该一起转,这些字节应该怎么转。都不清楚。所以,在转换的过程中,就有可能出错。于是,就有了字符流,专门解决这个问题。

字符流 = 字节流 + 编码表;字符流的底层会用普通字节流进行读写,然后字符流仅仅是做了把字节与字符进行转换的事情。这样就不需要程序员自己来转了。就不会出错了!

编码表

编码表:可以看做是一个字典。这本字典翻译是 人类的字符 和 机器语言(二进制) 之间对应关系。

编码表:就是人类生活的字符和计算机二进制的对照关系表。

ASCII:最早的,美国人制定的一张码表。包含字母、数字、符号、拉丁文。它用1个字节的最低7位表示数据。

a 97 01100001

ISO8859-1:欧洲制定的码表。兼容ASCII。多了一些欧洲的语言。它用1个字节的全部来表示数据。没有未知字符的!

GB2312:识别数千中文

GBK:识别2万多中文,2个字节表示一个字符。

GB18030:GBK的升级

Unicode:世界计算机协会制定通用码表,2个字节表示一个字符。

UTF-8:Unicode升级版。能用1个字节表示的就用1个字节,要用两个字节的就用2个字节,能用3个字节表示的就用3个字节。汉字基本都是3个字节

编码和解码

     * 编码:把我们能看懂的变成看不懂的(字符 --> 字节)

     *     byte[] getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列

     *     byte[] getBytes(String charsetName)使用指定的字符集将此 String 编码为 byte 序列

     * 解码:把我们看不懂的变成能看懂的(字节 --> 字符)

     *     String(byte[] bytes) 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String

     *     String(byte[] bytes, String charsetName)通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String

     * 乱码:

     *     原因:编码和解码的码表不一致

public static void main(String[] args) throws UnsupportedEncodingException {
        encode();
	System.out.println("------------------");
	decode();
	System.out.println("------------------");
	luanCode();
}
// 演示:乱码
public static void luanCode() throws UnsupportedEncodingException {
	String str = "你好";
	// 用UTF-8编码
	byte[] bytes = str.getBytes("UTF-8");
	// 把这个数组给别人,让他解码,用GBK
	String s = new String(bytes,"GBK");
	System.out.println(s);// 浣犲ソ
		
	// 先用GBK编码
	byte[] bytes2 = s.getBytes("GBK");
	// 在用UTF-8解码
	String s2 = new String(bytes2,"UTF-8");
	System.out.println(s2);// 你好
}
// 演示:解码
public static void decode() throws UnsupportedEncodingException {
	// 准备字节数组
	byte[] gbk_bytes = {-60, -29, -70, -61};
	// String(byte[] bytes) 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String
	String s1 = new String(gbk_bytes);
	System.out.println(s1);// 你好
	
	byte[] utf_bytes = {-28, -67, -96, -27, -91, -67};
        String s2 = new String(utf_bytes,"UTF-8");
	System.out.println(s2);
}
// 演示:编码
public static void encode() throws UnsupportedEncodingException {
	String s = "你好";
	// 使用平台默认编码集进行编码
	byte[] bytes = s.getBytes();
	System.out.println(Arrays.toString(bytes));// [-60, -29, -70, -61]
	
	// 用GBK编码
	byte[] bgk_bytes = s.getBytes("GBK");
	System.out.println(Arrays.toString(bgk_bytes));// [-60, -29, -70, -61]
	// 用UTF-8编码
	byte[] utf_bytes = s.getBytes("UTF-8");
	System.out.println(Arrays.toString(utf_bytes));// [-28, -67, -96, -27, -91, -67]
}
           

运行结果:

IO流复习总结(三) 字符流、编码表

字符流

Writer是写入字符流的抽象类

OutputStreamWriter(输出转换流)是字符流通向字节流的桥梁,将要写入流的字符编码成字节。

构造方法摘要OutputStreamWriter(OutputStream out) 创建使用默认字符编码的 OutputStreamWriter。OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的 OutputStreamWriter。

字符流的底层还是用字节流进行读写,字符流仅仅是做了字节和字符的转换(编码和解码)。接收一个字节流,形成了字符流。把字节流转换为字符流!接收字符数据,首先要把字符数据变成字节数据,然后再用普通字节流,把数据写到硬盘。实现了字符数据转换为字节数据。

Writer的5种写出功能

     *     public void write(int c) 写出一个字符

     *     public void write(char[] cbuf) 写出字符数组

     *     public void write(char[] cbuf,int off,int len) 写出字符数组cbuf中,从off开始,共len个字符

     *     public void write(String str) 写出字符串

     *     public void write(String str,int off,int len)写出字符串,从off开始,共len个字符

public static void main(String[] args) throws IOException {
	// 创建输出流,关联目标文件
	OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw2.txt"));
	// 写出数据
	// public void write(int c) 写出一个字符
	osw.write('a');
	osw.write('你');
	osw.write('9');
	
	// public void write(char[] cbuf) 写出字符数组
	char[] cbuf = {'h','e','l','l','o','柳','岩'};
	osw.write(cbuf);
	
	// public void write(char[] cbuf,int off,int len) 写出字符数组cbuf中,从off开始,共len个字符
	// 需求:写出:lo柳岩
	osw.write(cbuf, 3, 4);
	
	String str = "helloworld";
	// public void write(String str) 写出字符串
	osw.write(str);
	// public void write(String str,int off,int len)写出字符串,从off开始,共len个字符
	// 需求:写出owo
	osw.write(str, 4, 3);
	// 释放资源
	osw.close();
}
           

写入结果:

IO流复习总结(三) 字符流、编码表

Reader是用于读取字符流的抽象类

InputStreamReader(输入转换流)是字节流通向字符流的桥梁,将要读取字节并将其解码成字符。

构造函数:InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReaderInputStreamReader(InputStream in, String charsetName)创建使用指定字符集的 InputStreamReader。

字符流底层还是用的字节流,字符流仅仅做字符和字节的转换!接收一个字节流,生成的一个字符流。把字节流转换为字符流。底层会用字节流去硬盘读取字节数据,把字节数据转换为字符数据,然后返回。实现了把字节数据转换为字符数据。

字符流复制文本文件

使用字符转换流复制文本文件,复制EncodeDecodeDemo.java 到 copy.java

public static void main(String[] args) throws IOException {
	// 创建输入流,关联源文件
	InputStreamReader isr = new InputStreamReader(new FileInputStream("EncodeDecodeDemo.java"));
	// 创建输出流,关联目标文件
	OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("copy.java"));
	// 读写数据
	// 一次一个字符
//	int c = 0;
//	while((c = isr.read()) != -1){
//		osw.write(c);
//	}
	// 一次多个字符
	char[] cbuf = new char[1024];
	int len = 0;
	while((len = isr.read(cbuf)) != -1){
		osw.write(cbuf, 0, len);
	}
	// 释放资源
	isr.close();
	osw.close();
}
           

转换流的便捷形式:FileWriter和FileReader

/*
 * 演示:FileWriter的使用
 * 	public FileWriter(String pathname)
 * 	public FileWriter(File file)
 * 	public FileWriter(String pathname,boolean append)
 * 	public FileWriter(File file,boolean append)
 */
public class FileWriterDemo01 {
	public static void main(String[] args) throws IOException {
		// 创建输出流,关联目标文件
		FileWriter fw = new FileWriter("fw.txt");
		// 写出数据
		fw.write("hello,虎哥");
		// 释放资源
		fw.close();
	}
}
           
/*
 * 演示:FileReader便捷类的使用
 * 	public FileReader (String pathname)
 * 	public FileReader (File file)
 */
public class FileReaderDemo01 {
	public static void main(String[] args) throws IOException {
		// 创建输入流,关联源文件
		FileReader fr = new FileReader("copy.java");
		// 读取数据
		char[] cbuf = new char[1024];
		int len = 0;
		while((len = fr.read(cbuf)) != -1){
			System.out.print(new String(cbuf,0,len));
		}
		// 释放资源
		fr.close();
	}
}
           

便捷类复制文件

public class CopyFileDemo {
	public static void main(String[] args) throws IOException {
		// 创建输入流,关联源文件
		FileReader fr = new FileReader("copy.java");
		// 创建输出流,关联目标文件
		FileWriter fw = new FileWriter("copy2.java");
		// 读写数据
		char[] cbuf = new char[1024];
		int len = 0 ;
		while((len = fr.read(cbuf)) != -1){
			fw.write(cbuf, 0, len);
		}
		// 释放资源
		fr.close();
		fw.close();
	}
}
           

便捷类和父类的关系

父类:

    OutputStreamWriter = OutputStream + 任意的编码表

    InputStreamReader = InputStream + 任意编码表

子类:

    FileWriter = FileOutputStream + 本地默认编码表

    FileReader = FileInputStream + 本地默认编码表

字符缓冲流

为了高效的进行读写,Java提供了字符的缓冲流,也就是在缓冲流的内部维护了缓冲数组,这样以来就能实现字符数据的高效读写!普通字符流,内部有一个缓冲数组,这是一个字节数组。作用是减少了流与硬盘之间的交互次数。提高了效率。缓冲字符流,内部有一个缓冲数组,这是一个字符数组。有什么作用?

普通字符流,每次write都会调用一次编码转换器,调用编码转换器,本身也是非常消耗资源的!

缓冲字符流,当我们需要write时,不会去调用编码转换器,而是把要写的字符存入缓冲区,等缓冲区中足够多,然后一次性调用编码转换器,把数据转为字节。这样就减少了调用编码转换器的次数,从而提高了效率。

BufferedReader的特有功能:String readLine()读取一个文本行

// 普通方法复制
public static void method_01() throws IOException {
	// 创建输入流,关联源文件
	BufferedReader br = new BufferedReader(new FileReader("copy.java"));
	// 创建输出流,关联目标文件
	BufferedWriter bw = new BufferedWriter(new FileWriter("copy3.java"));
	// 读写数据
	char[] cbuf = new char[1024];
	int len = 0;
	while ((len = br.read(cbuf)) != -1) {
		bw.write(cbuf, 0, len);
		bw.flush();
	}
	// 释放资源
	br.close();
	bw.close();
}
           
// 特有功能复制
public static void method_02() throws IOException {
	// 创建输入流,关联源文件
	BufferedReader br = new BufferedReader(new FileReader("copy.java"));
	// 创建输出流,关联目标文件
	BufferedWriter bw = new BufferedWriter(new FileWriter("copy4.java"));
	// 读写数据
	String line = null;
	while((line = br.readLine()) != null){
		bw.write(line);
		bw.newLine();
		bw.flush();
	}
	// 释放资源
	br.close();
	bw.close();
}
           

IO流的总结

1、IO流的继承体系

InputStream:字节输入流:FileInputStream、BufferedInputStream

OutputStream:字节输出流FileOutputStream、BufferedOutputStream

    Reader:字符输入流InputStreamReader、FileReader、BufferedReader

Writer:字符输出流OutputStreamWriter、FileWriter、BufferedWriter

2、分清构造函数

1)四大基本流,他们都是直接关联文件或路径名FileInputStream、FileOutputStream、FileReader、FileWriter

2)装饰流,他们的构造函数接收本体系的顶级父类BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter

3)转换流,构造函数,接收对应的字节流顶级父类。InputStreamReader、OutputStreamWriter

3、如何选择具体的流?

原则:先选择基本流。

问题1:我是字节数据还是字符数据

字节数据:FileInputStream\FileOutputStream

字符数据:FileReader\FileWriter

问题2:我是读还是写?

读:FileInputStream\FileReader

写:FileOutputStream\FileWriter

问题3:我是否需要高效缓冲呢?

是:我就用选择的这个流对应体系的缓冲流包起来。

问题4:如果需要指定编码表,那么可以考虑用父类替换对应的便捷类。