Java核心API之字符流使用介绍
1、字符流原理
字符流是以字符为单位读写数据的流。Reader是字符输入流的父类,Writer是字符输出流的父类。
字符流是一种高级流,其构造方法需要传入一种低级流作为参数。
字符流底层封装的仍是字节流。
2、InputStreamReader的构造方法
InputStreamReader有如下几个构造方法,但是最常用的是InputStreamReader(InputStream in, String charsetName),改构造方法第一个参数是字节流,第二个参数是字符集字符串
代码片段如下:
/*
* InputStreamReader构造方法
* InputStreamReader(InputStream in)
*
* InputStreamReader(InputStream in, String charsetName)
*
* InputStreamReader(InputStream in, Charset cs)
*
* InputStreamReader(InputStream in, CharsetDecoder dec)
*/
@Test
public void testISRContructor() throws Exception{
FileInputStream fis = new FileInputStream("rw.txt");
//构造方法InputStreamReader(InputStream in)参数传入一个字节流
InputStreamReader isr = new InputStreamReader(fis);
//构造方法InputStreamReader(InputStream in, String charsetName)
//第一个参数是字节流,第二个参数是字符集字符串
InputStreamReader isr2 = new InputStreamReader(fis,"UTF-8");
//构造方法InputStreamReader(InputStream in, Charset cs)
//第一个参数是字节流,第二个参数是字符集类
Charset cs = Charset.forName("UTF-8");
InputStreamReader isr3 = new InputStreamReader(fis,cs);
//构造方法InputStreamReader(InputStream in, CharsetDecoder dec)
//第一个参数是字节流,第二个参数是编码器
CharsetDecoder dc = cs.newDecoder();
InputStreamReader isr4 = new InputStreamReader(fis,dc);
}

3、InputStreamReader的原理
该流能够通过给定的字符集将读取的字节数组转换成字符串,当然该流将底层封装其底层仍是基于字节流的,我们能够根据给定的字符集读取到相应的字符串。
示例代码如下:
/*
* InputStreamReader字符输入流常用方法
* int read()
* 读取一个字符,返回的int值"低16位"有效,不是int值"低8位"有效了
*
* int read(chra[] chrs)
* 读取一组字符数组,返回值是实际读取的字符量
*/
@Test
public void testISR() throws Exception{
FileInputStream fis = new FileInputStream("test_inputstreamreader.txt");
//构造方法InputStreamReader(InputStream in,String charsetName)
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
int d = -1;
while((d = isr.read())!=-1){
System.out.print((char)d);
}
isr.close();
}
字符输入流的read()让人似曾相识,只是返回值是“低16位”(一个字符占位)有效,而read(char[] chrs)是读取字符数组,而不是字节数组了。
分析:关闭高级流,会自动关闭低级流。所以不必再写fis.close()。

4、OutputStreamWrite的构造方法
OutputStreamWriter的构造方法和InputStreamReader的构造方法是一一对应的,这里就不多赘言。
代码片段如下:
/*
* OutputStreamWriter的几个构造方法:
* OutputStreamWriter(OutputStream out)
*
* OutputStreamWriter(OutputStream out, String charsetName)
*
* OutputStreamWriter(OutputStream out, Charset cs)
*
* OutputStreamWriter(OutputStream out, CharsetEncoder enc)
*/
@Test
public void testOSWContructor() throws Exception{
FileOutputStream fos = new FileOutputStream("rw.txt");
//构造方法OutputStreamWriter(OutputStream out)
OutputStreamWriter osw = new OutputStreamWriter(fos);
//构造方法OutputStreamWriter(OutputStream out, String charsetName)
OutputStreamWriter osw2 = new OutputStreamWriter(fos,"UTF-8");
//构造方法OutputStreamWriter(OutputStream out, Charset cs)
Charset cs = Charset.forName("UTF-8");
OutputStreamWriter osw3 = new OutputStreamWriter(fos,cs);
//构造方法OutputStreamWriter(OutputStream out, CharsetEncoder enc)
CharsetEncoder enc = cs.newEncoder();
OutputStreamWriter osw4 = new OutputStreamWriter(fos,enc);
}

5、OutputStreamWriter的原理
该流可以将字符串按照给定的字符集转换为字节后写出。
示例代码片段如下:
/*
* void write(int c)
* 写出int值的"低16位"有效,即一个字符
*
* void write(String str)
* 将给定的字符串写出
*
* void write(char[] chs)
* 将给定的字符数组写出
*
* void write(char[] chs,int off,int len)
* 将给定的字符数组以off为起始连续len个写出
*/
@Test
public void testOSW() throws Exception{
FileOutputStream fos = new FileOutputStream("test_outputsteamwriter.txt");
//构造方法OutputStreamWriter(OutputStream out,String charsetNa
me)
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
osw.write("Hello!OutputStreamWriter!");
System.out.println("写出完毕");
osw.close();
}
字符流的write方法与之前讲到的IO操作流相似,只是之前讲到的都是以字节为单位读写数据,现在是以字符为单位读取数据了。
注意:字符流只能用于文本文档文件,不能用于音频和视频等非文本文档文件。

6、使用字符流读取复制文件源代码分享
1)、main方法进行测试,示例代码如下:
/*
* 这次引入异常处理的概念,下节会继续讲到
*
*/
public static void main(String[] args){
//可以把抽象路径作为参数
copy("speech.txt","speech_copy.txt");
System.out.println("复制完毕");
}

7、2)、将复制文件的功能封装在一个copy方法中对调用者开放必要参数,复制文件copy方法如下
//复制文件
public static void copy(String docName,String copyName){
FileInputStream fis = null;
InputStreamReader isr = null;
FileOutputStream fos = null;
OutputStreamWriter osw = null;
try {
//读取文件
fis = new FileInputStream(docName);
isr = new InputStreamReader(fis,"UTF-8");
//复制文件
fos = new FileOutputStream(copyName);
osw = new OutputStreamWriter(fos,"UTF-8");
int d = -1;
while((d=isr.read())!=-1){
osw.write(d);
}
} catch (FileNotFoundException fnfE) {
System.out.println(docName+"文件不存在,请检查输入的文件名称或路径");
fnfE.printStackTrace();
} catch (UnsupportedEncodingException unEncodingE){
System.out.println("编码集无法编译");
unEncodingE.printStackTrace();
} catch (IOException ioE){
System.out.println("读写文件出错");
ioE.printStackTrace();
} finally {
if(isr!=null){
try {
isr.close();
} catch (IOException e) {
System.out.println("字符输入流关闭异常");
e.printStackTrace();
}
}
if(osw!=null){
try {
osw.close();
} catch (IOException e) {
System.out.println("字符输出流关闭异常");
e.printStackTrace();
}
}
}
}
查看运行结果,见图:
分析:项目研发过程中首先要确立需求,将代码模块化,便于单元测试和最终的集成测试,适当的注释能够帮助阅读代码。
