java程序员从笨鸟到菜鸟之(三十六)IO流之字符流

来源:互联网 发布:手机淘宝如何举报店铺 编辑:程序博客网 时间:2024/06/05 16:51

上一篇我们提到过,用字节流读取(文本文件)汉字打印在控制台上,会出现乱码,为解决这个问题引入了字符流

字符流是建立在字节流的基础上,能够提供字符层次的编码和解码

编码:将字符数据转换为字节数据(输入流--读数据);解码:将字节数据转换为字符数据(输出流--写数据)

类比:参考字符串的编码和解码

现在用字符流来读取处理不会出现乱码,是因为读取的不再是单个字节,而是能代表字符的多个字节,打印在控制台时,会通过编码表找到对应的字符。

注意1:默认采用Unicode编码,即在java里一个字符是由2个字节组成,所以对应的是0-65535之间(一个字节--0-255之间),会将指定格式的字符(对应的字节)转换为Unicode字符存储

注意2:指定的输入流的编码格式必须与要读的文本文件的编码格式一致

Reader和Writer类---抽象类----与编码方式有关类

 字符输入流读数据的方式:
   public int read():一次读取一个字符
   public int read(char[] chs):一次读取一个字符数组

 字符输出流写数据的方法:
   public void write(int c):写单个字符
   public void write(char[] cbuf):写一个字符数组
   public abstract void write(char[] cbuf,int off,int len):写入字符数组的一部分
   public void write(String str):写字符串
   public void write(String str, int off,int len):写字符串的一部分

转换流----字符流与字节流的桥梁----处理流

InputStreamReader(InputStream in):字符转换输入流

说明:读取一个字符,实际上读取的是该字符在此编码下(自己设置编码方式)对应的字节,然后转换为java平台默认的Unicodeb编码格式的字节,然后为该字符分配Unicodeb编码格式对应字节的内存空间。

注意:那么计算机是如何识别字符的?打印在控制台不会出现乱码?通过编码表(由字符及其对应数值组成的一张表)来唯一识别字符
OutputStreamWriter(Outputstream out):字符转换输出流

说明:对于每一个写入的字符,都会把它转换成指定的字符编码或者默认的方式,也即向输出流写入对应字符编码的字节

注意:从转换流得到的字符流,它读取的字符必须在编码表可以查到,否则会出现乱码,对于像图片、视频这样的文件不适宜用字符流来处理

实例1   一次读取一个字符

package 测试2;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;/** * 字符输入流读数据的方式: *    public int read():一次读取一个字符 */public class InputStreamReaderDemo {public static void main(String[] args) throws IOException {//1)创建字符输入流对象InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//2)方式1:一次读取一个字符/* * 说明:好----Unicode编码:89(高八位)、125(低八位) * 对应的Int值为22909 */int ch = 0 ;//read()返回值--每个字符对应的Unicode编码对应的字节的Int值while((ch=isr.read())!=-1){    System.out.print(ch);    System.out.print((char)ch);}//释放资源isr.close() ;}}
实例2   一次读取一个字符数组

package 测试2;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;/** * 字符输入流读数据的方式: * public int read(char[] chs):一次读取一个字符数组 *  */public class InputStreamReaderDemo {public static void main(String[] args) throws IOException {//1)创建字符输入流对象InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//2)方式2:一次读取一个字符数组char[] chs = new char[1024] ;//字符数组缓冲区int len = 0 ;//字符缓冲区中读取字符的实际个数while((len=isr.read(chs))!=-1){System.out.println(new String(chs, 0, len));}/* * 说明:当文本中的字符数超过1024个时, * 比如说1025个,那么下次实际读取的字符数是一个 * 为了避免打印空白等情况采用了String(chs, 0, len)的形式 * 打印字符缓冲区中真正的字符数 *///释放资源isr.close() ;}}
实例3  一次写入一个字符(文件复制)

package 测试2;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;public class InputStreamReaderDemo {public static void main(String[] args) throws IOException {//1)创建字符输入流、输出流对象InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"));//2)方式1:一次读取一个字符,写入一个字符int ch = 0 ;//while((ch=isr.read())!=-1){osw.write(ch);//写入字符}//释放资源isr.close() ;osw.close();}}
实例4  一次写入一个字符数组(文件复制)

package 测试2;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;public class InputStreamReaderDemo {public static void main(String[] args) throws IOException {//1)创建字符输入流对象InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"));//2)方式2:一次写入一个字符数组char[] chs = new char[1024] ;//字符数组缓冲区int len = 0 ;//字符缓冲区中读取字符的实际个数while((len=isr.read(chs))!=-1){osw.write(chs, 0, len);}//释放资源isr.close() ;osw.close();}}
注意:如果不采用平台默认的编码格式,例如采用utf-8的编码格式,会发现复制文件后,打开文件会出现乱码(不影响我们通过读取识别)

原因:就是系统的编码和程序的编码采用了不同的编码格式

使用字符转换流进行操作数据的时候:字节流+编码格式(默认GBK),在书写代码名称非常长,Java提供了中更简单的类--便捷类

说明:便捷类是InputStreamReader和OutputStreamWriter的子类

FileWrite和FileReader类

特点(局限性):只能按照本地平台的字符编码(GBK),不能用户指定字符编码类型

实例5

package org.westos_09;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;public class FileReaderDemo {public static void main(String[] args) throws IOException {//1)封装数据源和目的地FileReader fr = new FileReader("a.txt") ;FileWriter fw = new FileWriter("b.txt") ;//一次读取一个字符数组char[] chs = new char[1024] ;int len = 0 ;while((len=fr.read(chs))!=-1){fw.write(chs, 0, len) ;fw.flush() ;}//关闭资源fw.close() ;fr.close() ;}}
由于便捷类的性能达不到人们的需求,为了提高读写速度,Java就提供了一个字符缓冲流的类

   BufferedReader:字符缓冲输入流
   BufferedWriter:字符缓冲输出流
  将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 
   通过构造方式可以指定缓冲区的大小,或者接受默认的大小

                常见读取方式:读取字节、读取字节数组
  构造方式:
  public BufferedWriter(Writer out):创建默认缓冲区大小的一个字符缓冲输出流

                关于字符缓冲输入流的特有功能:
                BufferedWriter:
   public void newLine():写入一个换行符号(等价==“\r\n”)
                BufferReader:
   public String readLine():一次读取一行(内容,字符串)

实例6  缓冲区读写

package org.westos_10;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;public class BufferedDemo {public static void main(String[] args) throws IOException {write();read();}private static void read() throws FileNotFoundException, IOException {//代码重复度高,使用循环改进String line = null ;while((line=br.readLine())!=null){System.out.println(line);}//释放资源br.close() ;}private static void write() throws IOException {//写数据//创建一个字符缓冲输出流对象BufferedWriter bw = new BufferedWriter(new FileWriter("bw2.txt")) ;//写数据for(int x = 0 ; x <10 ; x ++){bw.write("hello"+x) ;//没有使用这个方法之前:使用写入换行符号//bw.write("\r\n") ;//使用特有功能bw.newLine() ;//刷新该流bw.flush() ;//必须刷新!!!}//释放资源bw.close() ;}}
字符缓冲区流作用机理:由于带有缓冲区把一批数据写入到缓冲区内,当缓冲区满的时候,才把缓冲区的数据写入到字符缓冲流中

好处:避免每次都执行物理写操作,从而提高了I/O操作的效率

缺点:字符缓冲区的数据不满,会一直等待,如果不刷新就无法从缓冲区把数据写入到文件中

因此必须强制刷新









 

原创粉丝点击