转换流:
在《Java网络编程》中,有这样一段话:
“Reader和Writer最重要的子类是InputStreamReader和OutputStreamWriter类。
InputStreamReader类包含了一个底层输入流,可以从中读取原始字节。它根据指定的编码方式,将这些字节转换为Unicode字符。
OutputStreamWriter从运行的程序中接收Unicode字符,然后使用指定的编码方式将这些字符转换为字节,再将这些字节写入底层输出流中。”
基本知识:
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本f符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。
所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列。
字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的,但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联 在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。
字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串; 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以。
字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点。
字节字符转换流
转换流的类:
- InputStreamReader:字节字符输入转换流
- OutputStreamWriter:字节字符输出转换流
转换流是需要考虑字符集编码解码问题,不指定采用的是默认的字符集编码格式
字符操作和字节操作存在关系?
OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。字符转换流原理:字节流+编码表。
继承关系:子类:
OutputStreamWriter:|–FileWriter:
InputStreamReader:|–FileReader;
FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。 当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。
以下三种方法效果相同:
(1)InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”));//默认字符集。
(2)InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”),“GBK”);//指定GBK字符集。
(3)FileReader fr = new FileReader(“a.txt”);
转换流的特点:
- 其是字符流和字节流之间的桥梁
- 可对读取到的字节数据经过指定编码转换成字符
- 可对读取到的字符数据经过指定编码转换成字节
InputStreamReader类
转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
源码分析
继承结构
public class InputStreamReader extends Reader
参数属性
private final StreamDecoder sd;
构造方法
//InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
//InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
父类:
//流对象
protected Object lock;
父类构造方法:
protected Reader(Object lock) {
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}
构造举例,代码如下:
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
IputStreamReader流方法
1)返回当前使用的编码集
public String getEncoding() {
return sd.getEncoding();
}
2)读流中一个字符,本质是调用StreamEncoder的read()方法
public int read() throws IOException {
return sd.read();
}
3)读流中字符数组大小的一部分,本质是调用StreamEncoder的read(char cbuf[], int off, int len)方法
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
4)返回当前流是否已经可读,本质是调用StreamEncoder的ready()方法
public boolean ready() throws IOException {
return sd.ready();
}
5)关闭当前流,本质是调用StreamEncoder的close()方法
public void close() throws IOException {
sd.close();
}
关于StreamEncoder流的详解:
https://blog.csdn.net/ai_bao_zi/article/details/81181198
OutputStreamWriter类
转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集讲字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
源码分析
继承结构
public class OutputStreamWriter extends Writer
参数属性
//构建一个StreamEncoder流se
private final StreamEncoder se;
构造方法
//OutputStreamWriter(OutputStream out): 创建一个使用默认字符集的字符流。
public OutputStreamWriter(OutputStream out) {
super(out);
try {
se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
//OutputStreamWriter(OutputStream out, tring charsetName): 创建一个指定字符集的字符流。
public OutputStreamWriter(OutputStream out, String charsetName)
throws UnsupportedEncodingException
{
super(out);
if (charsetName == null)
throw new NullPointerException("charsetName");
se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
}
父类构造方法类比reader
构造举例,代码如下:
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
OutputStreamWriter流方法
1)写入一个字符到流中:本质是调用StreamEncoder的write(int c)方法
public void write(int c) throws IOException {
se.write(c);
}
2)写入字符数组的一部分到流中:本质上调用StreamEncoder的write(char cbuf[], int off, int len)方法
public void write(char cbuf[], int off, int len) throws IOException {
se.write(cbuf, off, len);
}
3)写入字符串的一部分到流中:本质是调用StreamEncoder的write(String str, int off, int len)方法
public void write(String str, int off, int len) throws IOException {
se.write(str, off, len);
}
4)刷新流中的数据:本质是调用StreamEncoder的flush()方法
public void flush() throws IOException {
se.flush();
}
5)关闭流但是先刷新流:即该流必须要先调用flush方法然后再调用close方法
public void close() throws IOException {
se.close();
}
为了获得最高效率,请考虑在BufferedWriter中包装OutputStreamWriter,以避免频繁的转换器调用
参考博文:https://blog.csdn.net/ai_bao_zi/article/details/81168420