字符流
字符流结构图:
为什么需要字符流,用字节流不可么?
通过字节流来进行文件复制的话是没有任何问题的,但是,当你想通过程序来观看文件的内容时(比如将文件中的内容打印到控制台)就很大几率出现乱码现象,这是为什么呢?如果文件中只有ASCII码表中的字符是没有任何问题的,但是如果文件中包含中文的话就有乱码现象出现,这是因为在给文本进行编码和解码的编码表不同造成的,或者是通过一个字节的方式来读取内容的,因为中文是用gb系列编码或Unicode编码,在gb系列编码中中文占用两个字节,在Unicode中中文至少占用3个字节,当你是用字节流进行读取,字节流以字节为单位传输,并不知道你所读取的内容是什么编码类型,所以也就无法正确解读;为了解决乱码问题,让我们能够方便的阅读,字符流就可以很好的解决这一问题;
字符流 = 字节流 + 编码表;
编码表:
ASCII表:a --> 97,A --> 65, 0 --> 48
GBK编码:一个中文占两个字节,包含ASCII中的所有字符;
UTF-8编码:一个中文占3个字节,可以兼容世界上很多国家的语言;国际通用编码;
编码和解码:
-
编码:
- 表面含义:就是把我们能看懂的转成我们看不懂的;
- 本质:本质就是把字符串转成字节数组;
- 方法:
byte[] getBytes() : 使用平台(idea/Eclipse)的默认字符集将该 String编码为一系列字节 byte[] getBytes(String charsetName) : 使用指定的字符集将该 String编码为一系列字节
-
解码:
- 表面含义:就是把看不懂的转成我们能看懂的;
- 本质:本质就是把字节数组转成字符串;
- 方法:
String(byte[] bytes) : 使用平台的默认字符集解码指定的字节数组来创建字符串 String(byte[] bytes, String charsetName) : 通过指定的字符集解码指定的字节数组来创建字符串
-
出现乱码的原因:
编码和解码使用的编码表不一致;
字符输入流(Reader)
概述:
Reader是字符输入流的抽象根类,其子类有InputStreamReader
、FileReader
、BufferedReader
等;
常用方法:
方法 | 方法说明 |
---|---|
int read() | 从源文件中读取一个字符的数据 |
int read(char[] cbuf) | 从源文件中读取一个字符数组的数据 |
int read(char[] cbuf, int offset, int length) | 从源文件中读取一个字符数组的一部分数据 |
void close() | 关闭与流相关的资源 |
字符输出流(Writer)
概述:
Writer是字符输出流的抽象根类,其子类有OutputStreamWriter
、FileWriter
、BufferedWriter
等;
常用方法:
方法 | 方法说明 |
---|---|
void write(int c) | 向目标文件写入一个字符 |
void write(char[] cbuf) | 向目标文件中写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 向目标文件中写入一个字符数组的一部分 |
void write(String str) | 向目标文件中写入一个字符串 |
void write(String str, int off, int len) | 向目标文件中写入一个字符串的一部分 |
void flush() | 将数据从缓冲区写入到目标文件中 |
void close() | 先将数据从缓冲区写入到目标文件中,然后释放资源 |
转换流
概述:
转换流是字节流转换成字符流的桥梁,它可以根据指定的字符编码来将字节流转换成字符流,也可以采用平台默认的字符编码;
转换输入流(InputStreamReader)
概述:
InputStreamReader是将字节输入流转换成字符输入流;其继承自Reader抽象类,所以拥有Reader中的所有方法;
构造方法:
InputStreamReader(InputStream in) : 使用默认字符编码创建InputStreamReader对象
InputStreamReader(InputStream in,String chatset) : 使用指定的字符编码创建InputStreamReader对象
转换输出流(OutputStreamWriter)
概述:
OutputStreamWriter可以将字节输出流转换成字符缓冲流;其继承自Writer抽象类,所以拥有Writer中的所有方法;
构造方法:
OutputStreamWriter(OutputStream out) : 使用默认字符编码创建OutputStreamWriter对象
OutputStreamWriter(OutputStream out,String charset) : 使用指定的字符编码创建OutputStreamWriter对象
将字符串写入文件后读出
import java.io.*;
public class Demo01 {
public static void main(String[] args) throws IOException {
// 创建目标文件路径
String src = "IO\\a.txt";
// 创建输出流
Writer writer = new OutputStreamWriter(new FileOutputStream(src));
// 创建输入流对象
Reader reader = new InputStreamReader(new FileInputStream(src));
// 具体操作
writer.write("你好,中国!");
writer.flush(); // 这个必须要有,否则会造成IO阻塞,导致reader读取不成功
char[] chars = new char[1024];
int len;
while ((len = reader.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
// 关闭资源
reader.close();
writer.close();
}
}
字符流
字符输入流和字符输出流
概述:
FileReader:字符输入流,其父类是转换输入流,顶层父类是Reader,拥有Reader抽象类中的所有方法;
FileWriter:字符输出流,其父类是转换输出流,顶层父类是Writer,拥有Writer抽象类中的所有方法;
构造方法:
// FileReader构造方法
FileReader(String fileName) // 通过文件路径创建字符输入流对象
FileReader(File file) // 通过文件对象创建字符输入流对象
// FileWriter构造方法
FileWriter(String fileName) // 通过文件路径创建字符流输出流对象
FileWriter(File file) // 通过文件对象创建字符流输出流对象
字符流和转换流的区别:
- 构造方法参数不同:转换流构造方法传入的是字节流,字符流构造方法传入的是文件的路径或文件的对象;
- 转换流可以指定编码去读和写,而字符流只能使用平台默认的编码去读和写;
代码示例:
import java.io.*;
public class Demo04 {
public static void main(String[] args) throws IOException {
// 创建源文件路径
String source = "IO\\a.txt";
// 创建目标文件路径
String target = "IO\\b.txt";
// 创建输入流对象
Reader reader = new FileReader(source);
// 创建输出流对象
Writer writer = new FileWriter(target);
// 具体操作
char[] chars = new char[1024];
int len;
while ((len = reader.read(chars)) != -1) {
writer.write(new String(chars, 0, len));
}
// 释放资源
writer.close();
reader.close();
}
}
字符缓冲流
概述:
字符缓冲流分为字符缓冲输入流和字符缓冲输出流,其和字节缓冲流类似,其提供一个默认长度为8192的字符数组;它们拥有Writer和Reader中的所有方法,与此同时还有自己特有的新方法;
BufferedWriter
-
构造方法:
BufferedWriter(Writer out) // 创建字符缓冲输出流对象
-
特有方法:
void newLine() // 写一行行分隔符,行分隔符字符串由系统属性定义
BufferedReader
-
构造方法:
BufferedReader(Reader in) // 创建字符缓冲输入流对象
-
特有方法:
String readLine() // 读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null,不能读取换行符号
字符缓冲流复制文件:
import java.io.*;
public class Demo07 {
public static void main(String[] args) throws IOException {
// 创建源文件路径
String source = "IO\\a.txt";
// 创建目标文件路径
String target = "IO\\b.txt";
// 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader(source));
// 创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter(target));
// 具体操作
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
// 释放资源
bw.close();
br.close();
}
}