这个连接包含了常用的流------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文件是有了,但是我们打开的时候显示一下内容


字符流(InputStreamReader,OutputStreanWriter,FileReader,FileWriter)_IO流


因为字符流是读取字节后,缓存,然后去码表查找匹配,
若是匹配不到就会到位置码表位置寻找类似的,此时返回的数据就可能不正确。
因此生成的新图片的编码和老图片的编码是不一致的,从而导致图片不能正常显示。
所以不要用字符流拷贝媒体文件,字符流只能用来操作纯文本类型的文件,比如一个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();
}
}


到这里,基本字符流就介绍完了