1.概述
在IO包中实际上分为字节流和字符流,但是除了这两个流之外,还存在一组字节流-字符流的转换类。
①. OutputStreamWriter:是Writer的子类,将输出的字符流变成字节流,即将一个字符流的输出对象变为字节流输出对象。
②. InputStreamReader:是Reader的子类,将输入的字节流变成字符流,即将一个字节流的输入对象变成字符流的输入对象。
如果以文件操作为例,则内存中的字符数据需要通过OutputStreamWriter变成字节流才能保存在文件中,读取时需要将输入的字节流通过InputStreamReader变成字符流,转化步骤如图:
从图中可以清晰地发现,不管如何操作,最终全部是以字节的形式保存在文件中。
2.OutputStreamWriter和InputStreamReader的构造方法以及常用方法
OutputStreamWriter的构造方法:
// 创建使用默认字符编码的 OutputStreamWrite,默认编码表GBK
public OutputStreamWriter(OutputStream out)
// 创建使用指定字符集的 OutputStreamWriter。
public OutputStreamWriter(OutputStream out, String charsetName) throws UnsupportedEncodingException
OutputStreamWriter的方法:
// 刷新该流的缓冲。
public void flush() throws IOException
// 关闭此流,但要先刷新它。实际上它调用了父类Writer的close()方法
public void close() throws IOException
// 写入字符数组的某一部分
public void write(char[] cbuf, int off, int len) throws IOException
InputStreamReader的构造方法:
// 创建一个使用默认字符集的 InputStreamReader
public InputStreamReader(InputStream in)
// 创建使用指定字符集的 InputStreamReader
public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException
InputStreamReader的方法:
// 关闭该流并释放与之关联的所有资源,和OutputStreamWriter一样,调用了Reader类的close()方法
public void close() throws IOException
// 将字符读入数组中的某一部分。
public int read(char[] cbuf, int offset, int length) throws IOException
3.转化流示例
示例一:将字节输出流变成字符输出流Writer out = new OutputStreamWrite(new FileOutputStream(new File("..."))); // 字节流变为字符流
示例二:将字节输入流变成字符输入流
Reader in = new InputStreamReader(new FileInputStream(new File("...")));
范例:从键盘读取数据,转化大写,显示在屏幕上。
BufferedReader reader = new BufferedReader(new InputStreamReader(
System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
System.out));
String line = null;
while ((line = reader.readLine()) != null) {
if ("over".equals(line))
break;
writer.write(line.toUpperCase());
writer.newLine();
writer.flush();
}
// 因为是从System获取的流可以不关闭,随着系统的结束而结束
writer.close();
reader.close();
二、IO流的操作规律
1. 明确数据源和目的
源:输入流 InputStream、Reader 一定是被读取的。
目的:输出流 OutputStream、 Writer 一定是被写入的。
2. 操作的数据是否为纯文本的数据?
纯文本:字符流 Reader、 Writer
非纯文本: 字节流 InputStream、OutputStream
如果是源并且是纯文本,Reader
如果是目的并且是纯文本,Writer
3. 当明确体系后,在明确要是使用那个具体的对象
①. 通过设备来区分:
源设备:键盘(System.in)、硬盘(FileReader、FileInputStream)、内存(ByteArrayInputStream ...)、网络(Socket)。
目的设备:显示器(控制台System.out)、硬盘(FileWriter、FileOutputStream)、内存(ByteArrayOutputStream ...)、网络(Socket)。
②.明确是否需要额外功能?
Ⅰ. 是否需要高效?缓冲区BufferedXXX
Ⅱ. 是否需要转化?转化流 InputStreamReader、OutputStreamWriter
Ⅲ. 是否操作基本数据类型?DataInputStream、DataOutputStream
Ⅳ. 是否操作对象?ObjectInputStream、ObjectOutputStream
Ⅴ. 需要对多个源合并吗?SequenceInputStream
Ⅵ. 需要保存数据的表现形式到目的地吗?PrintWriter
范例1: 复制一个文本文件。
根据以上的操作规律,明确需要什么对象。
Ⅰ. 明确源和目的:源(InputStream、 Reader)、目的(OutputStream、Writer)
Ⅱ. 明确是否为纯文本?是。源(Reader)、目的(Writer)
Ⅲ. 明确具体设备:源(硬盘)、目的(硬盘)
源对应的体系Reader中可以操作硬盘设备的对象是 FileReader
目的对应的体系Writer中可以操作硬盘设备的对象是FileWriter
Ⅳ. 需要额外功能码?需要高效,使用缓冲区
示例代码:
// 源
BufferedReader bufferedReader = new BufferedReader(new FileReader("source.txt"));
// 目的
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("target.txt"));
// 读写操作
String line = null;
while ((line = bufferedReader.readLine()) != null) {
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
// 关闭资源
bufferedWriter.close();
bufferedReader.close();
范例2:复制一个图片
Ⅰ. 明确源和目的:源(InputStream、 Reader)、目的(OutputStream、Writer)
Ⅱ. 明确是否为纯文本?否。源(InputStream)、目的(OutputStream)
Ⅲ. 明确具体设备:源(硬盘)、目的(硬盘)
源对应的体系InputStream中可以操作硬盘设备的对象是 FileInputStream
目的对应的体系OutputStream中可以操作硬盘设备的对象是FileOutputStream
示例代码:
FileInputStream fileInputStream = new FileInputStream("source.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("target.jpg");
int len;
byte[] bytes = new byte[1024];
while ((len = fileInputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, len);
}
fileOutputStream.close();
fileInputStream.close();
范例3: 读取键盘录入,存储到一个文件中。
Ⅰ. 明确源和目的:源(InputStream、 Reader)、目的(OutputStream、Writer)
Ⅱ. 明确是否为纯文本?是。源(Reader)、目的(Writer)
Ⅲ. 明确具体设备:源(键盘)、目的(硬盘)
具体对象:源(System.in)、目的(FileWriter)
Ⅳ. 需要额外功能码?需要高效,使用缓冲区; 需要转化流
实例代码:
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("target.txt"));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
if ("over".equals(line)) {
break;
}
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
bufferedWriter.close();
bufferedReader.close();
范例4:读取一个文本文件,将文件中文本按照指定的编码表UTF-8写入到另一个文件中。
Ⅰ. 明确源和目的:源(InputStream、 Reader)、目的(OutputStream、Writer)
Ⅱ. 明确是否为纯文本?是。源(Reader)、目的(Writer)
Ⅲ. 明确具体设备:源(硬盘(File))、目的(硬盘(File))
这样做不行,满足不了需求,为什么呢?
因为这个两个对象在操作文本数据,都是用了默认的编码表。本机中默认码表是GBK.
而需求中希望写入到文件的数据是按照utf-8的码表。
源对象不变。
FileReader fr = new FileReader("a.txt");
需要目的为指定编码表。
这时就要用到转换流。因为转换流中可以指定具体的编码表。 需要往里传递一个字节流,而且要操作文件,FileOutputStream
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("target.txt"),"UTF-8");