这个连接包含了常用的流------IO流(总篇章)
为什么会有字符流?
先看下面这个例子,b.txt文件里只有3个汉字:林高禄
package com.testIO;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author 林高禄
* @create 2020-05-09-17:26
*/
public class StreamDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("test//src//com//testIO//b.txt");
byte[] bys = new byte[1024];
int len;
while ((len=fis.read(bys))!= -1){
System.out.print(new String(bys,0,len));
}
fis.close();
}
}
输出:
林高禄
但是如果我们把字节数组的大小调整一下,改为2
package com.testIO;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author 林高禄
* @create 2020-05-09-17:26
*/
public class StreamDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("test//src//com//testIO//b.txt");
byte[] bys = new byte[2];
int len;
while ((len=fis.read(bys))!= -1){
System.out.print(new String(bys,0,len));
}
fis.close();
}
}
输出:
�������
为什么会这样呢?
因为一个汉字的存储:
- 如果是GBK编码,占用2个字节
- 如果是UTF-8编码,占用3个字节
而我的idea的默认编码是UTF-8编码,不管是UTF-8编码,还是GBK编码,字节流获取到的数据总会拆散汉字,所以输出结果不对,所以我们就引用了字符流。
我们先介绍下字符串与字符流的编码问题
字符串中的编码解码问题
编码
- byte[] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
- byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
解码
- String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
- String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
字符流中的编码解码问题
字符流抽象基类:
- Reader:字符输入流的抽象类
- Writer:字符输出流的抽象类
字符流中编码解码问题相关的两个类:
- InputStreamReader:从字节流到字符流的桥梁,它读取字节,并使用指定的charset将其解码为字符。它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
- OutputStreanWriter:从字符流到字节流的桥梁,使用指定的charset将写入的字符编码为字节。它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
基于上面出现的字节流出现的编码问题,我们把代码用字符流改进一下
package com.testIO;
import java.io.*;
/**
* @author 林高禄
* @create 2020-05-09-18:04
*/
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("test//src//com//testIO//b.txt"));
char[] chs = new char[2];
int len;
while ((len=isr.read(chs))!= -1){
System.out.print(new String(chs,0,len));
}
isr.close();
}
}
输出
林高禄
发现没有,我们接受输入的容器不再是字节数组,而是字符数组,从而也能准确的接收汉子呢,而不会出现编码问题
我们再做一个试验,用字符输出流,指定GBK编码将“中国”写入cc.txt,我们平台默认打开文本的编码是UTF-8
注意注释:// 因为是字符流,所以要刷新才能真正写入文本,但是close()方法内部已经帮我们flush(),所以这个可以省略
package com.testIO;
import java.io.*;
/**
* @author 林高禄
* @create 2020-05-09-18:04
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test//src//com//testIO//cc.txt"),"GBK");
osw.write("中国");
// 因为是字符流,所以要刷新才能真正写入文本,但是close()方法内部已经帮我们flush(),所以这个可以省略
//osw.flush();
osw.close();
}
}
打开cc.txt,里面的内容为
�й�
这乱码是必然的,因为我们是用GBK编码写进去,而用UTF-8编码查看
那么我们现在就用输入流,用GBK编码输入看看
package com.testIO;
import java.io.*;
/**
* @author 林高禄
* @create 2020-05-09-18:04
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test//src//com//testIO//cc.txt"),"GBK");
InputStreamReader isr = new InputStreamReader(new FileInputStream("test//src//com//testIO//cc.txt"),"GBK");
osw.write("中国");
char[] chs = new char[1];
int len;
while ((len=isr.read(chs))!= -1){
System.out.print(new String(chs,0,len));
}
isr.close();
osw.close();
}
}
输出:
(任何内容都没有输出)
为什么呢?思考一下,上面不是说了吗,因为是字符输出流,所以要刷新缓存,有些人就问了,上面的注释不是说了吗
// 因为是字符流,所以要刷新才能真正写入文本,但是close()方法内部已经帮我们flush(),所以这个可以省略
好好看看,我们在调输出流的close()方法之前,输入流已经开始输入了,也就是还没有刷新之前就开始输入了,里面还是空的,所以我们要刷新,或者输入流调用read()方法前调用close()方法,那么我们把代码调整一下
package com.testIO;
import java.io.*;
/**
* @author 林高禄
* @create 2020-05-09-18:04
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test//src//com//testIO//cc.txt"),"GBK");
InputStreamReader isr = new InputStreamReader(new FileInputStream("test//src//com//testIO//cc.txt"),"GBK");
osw.write("中国");
osw.flush();
char[] chs = new char[1];
int len;
while ((len=isr.read(chs))!= -1){
System.out.print(new String(chs,0,len));
}
isr.close();
osw.close();
}
}
输出:
中国
清楚了字符流之后,我们做一个例子,用字符流实现图片的复制
字符流读数据的2种方式:
- int read():一次读取一个字符数据
- int read(char[] cbuf):一次读一个字符数组数据
字符流写数据的5种方式:
- 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):写入一个字符串的一部分
package com.testIO;
import java.io.*;
/**
* @author 林高禄
* @create 2020-05-10-13:11
*/
public class CopyPng {
public static void main(String[] args) throws IOException{
InputStreamReader isr = new InputStreamReader(new FileInputStream("test//src//com//testIO//bb.png"));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test//src//com//testIO//bb1.png"));
char[] chs = new char[1024];
int len;
while ((len=isr.read(chs))!= -1){
osw.write(chs,0,len);
}
osw.close();
isr.close();
}
}
bb1.png文件是有了,但是我们打开的时候显示一下内容
因为字符流是读取字节后,缓存,然后去码表查找匹配,
若是匹配不到就会到位置码表位置寻找类似的,此时返回的数据就可能不正确。
因此生成的新图片的编码和老图片的编码是不一致的,从而导致图片不能正常显示。
所以不要用字符流拷贝媒体文件,字符流只能用来操作纯文本类型的文件,比如一个Java文件等。所以媒体文件还是用字节流拷贝字节缓冲流
了解了字符流之后,我们看到字符流的构造太长了。类名也长,所以我们就来介绍一下字符流的子类
-
FileReader:extends InputStreamReader,用于读取字符文件的便捷类,FileReader(String fileName)
-
FileWriter:extends OutputStreamWriter,用于写入字符文件的便捷类,FileWriter(String fileName)
这两个便捷子类不能指定字符编码和字节缓冲区的大小,只能是默认的,要修改这些值,还得使用InputStreamReader和OutputStreanWriter
package com.testIO;
import java.io.*;
/**
* @author 林高禄
* @create 2020-05-10-13:11
*/
public class Copy2 {
public static void main(String[] args) throws IOException{
FileReader fe = new FileReader("test//src//com//testIO//a.txt");
FileWriter fw = new FileWriter("test//src//com//testIO//a1.txt");
char[] chs = new char[1024];
int len;
while ((len=fe.read(chs))!= -1){
fw.write(chs,0,len);
}
fw.close();
fe.close();
}
}
到这里,基本字符流就介绍完了