字符流
我们字节流读字符数据,需要人工去做字节和字符数据的转换。我们自己转换,我们不清楚哪些字节应该一起转,这些字节应该怎么转。都不清楚。所以,在转换的过程中,就有可能出错。于是,就有了字符流,专门解决这个问题。
字符流 = 字节流 + 编码表;字符流的底层会用普通字节流进行读写,然后字符流仅仅是做了把字节与字符进行转换的事情。这样就不需要程序员自己来转了。就不会出错了!
编码表
编码表:可以看做是一个字典。这本字典翻译是 人类的字符 和 机器语言(二进制) 之间对应关系。
编码表:就是人类生活的字符和计算机二进制的对照关系表。
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]
}
运行结果:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL51ERPBTVU1kMNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL5YzN4IzM0YTM1IzMwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
字符流
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();
}
写入结果:
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:如果需要指定编码表,那么可以考虑用父类替换对应的便捷类。