Java-IO流(五)-字符流(OutputStreamWriter、InputStreamReader)

概述

字节--->字符 : 看不懂的--->看的懂的。  需要读。输入流。 InputStreamReader
字符--->字节 : 看的懂的--->看不懂的。  需要写。输出流。 OutputStreamWriter

OutputStreamWriter-指定编码写中文、InputStreamReader-指定编码读中文

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

public class TransStreamDemo {

    /**
     * @param args
     * @throws IOException 
     * @throws UnsupportedEncodingException 
     */
    public static void main(String[] args) throws Exception {
        
//      writeCN();
        
        readCN();
        /*
        总结:
        发现继承关系是这样的。
        OutputStreamWriter:
            |--FileWriter:
                
        InputStreamReader:
            |--FileReader;

        父类和子类的功能有什么区别呢?
        
        OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。
        字符转换流原理:字节流+编码表。
        
        FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。
        当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。
        
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
        FileReader fr = new FileReader("a.txt");
        这三句代码的功能是一样的,其中第三句最为便捷。
        
        注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。
        什么时候用子类呢?
        条件:
        1,操作的是文件。
        2,使用默认编码。
        
        字节--->字符 : 看不懂的--->看的懂的。  需要读。输入流。 InputStreamReader
        字符--->字节 : 看的懂的--->看不懂的。  需要写。输出流。 OutputStreamWriter
        
        
        */
    }

    public static void readCN() throws IOException {//解码
        
        //创建InputStreamReader对象。
        InputStreamReader isr = new InputStreamReader(new FileInputStream("tempfile/u8cn.txt"),"UTF-8");
        
        char[] buf = new char[1024];
        int len = isr.read(buf);
        System.out.println(new String(buf,0,len));
        isr.close();
    }

    //读取中文。
    public static void readCN_no() throws IOException {
        
        // 使用FileReader没出来,因为文件是UTF-8编码。读取UTF-8字节时,用该指定用UTF-8解码。
        // 说明需要指定码表。那就需要使用InputStreamReader。
        FileReader fr = new FileReader("tempfile/u8cn.txt");
//      int ch = (char)fr.read();
//      System.out.println((char)ch);
        char[] buf = new char[1024];
        int len = fr.read(buf);
        System.out.println(new String(buf,0,len));//浣犲ソ

        
        fr.close();
        
    }

    public static void writeCN() throws Exception {//编码
        //需求:既然识别中文的码表有两个,GBK UTF-8
        //能不能将中文数据按照utf-8的方式进行文件的存储呢?
        //还能使用FileWriter吗?不能使用了,因为FileWriter中默认的是GBK
        //通过FileWriter的api描述,要指定编码表这些值,需要使用OutputStreamWriter 
        //OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。
        //它的作用的就是,将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去。
        
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("tempfile/u8cn.txt"),"utf-8");
        
        osw.write("你好");//写入缓冲区。
        
        osw.close();
    }

}
Java-IO流(五)-字符流(OutputStreamWriter、InputStreamReader)_第1张图片
转换流的图解

字符流-复制文本文件

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyTextFileTest {

    /**
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {
        /*
         * 练习:复制文本文件。
         * 思路:
         * 1,既然是文本涉及编码表。需要用字符流。
         * 2,操作的是文件。涉及硬盘。
         * 3,有指定码表吗?没有,默认就行。
         * 操作的是文件,使用的 默认码表。使用哪个字符流对象。直接使用字符流操作文件的便捷类。FileReader  FileWriter
         */
        copyTextFile();
    }

    public static void copyTextFile() throws IOException {
        //1,明确源和目的。
        FileReader fr = new FileReader("Test24.java");
        FileWriter fw = new FileWriter("tempfile/test24_copy.txt");
        
        //2,为了提高效率。自定义缓冲区数组。字符数组。
        char[] buf = new char[1024];
        
        int len = 0;
        while((len=fr.read(buf))!=-1){
            fw.write(buf,0,len);
        }
        
        
        //2,循环读写操作。效率低。
//      int ch = 0;
//      while((ch=fr.read())!=-1){
//          fw.write(ch);
//      }
        
        //3,关闭资源。
        fw.close();
        fr.close();
        
    }

}

字符流-字符流缓冲区对象复制文本文件

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharStreamBufferedTest {

    /**
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {
        /*
         * 字符流中是否有提供缓冲区中。
         * 注意:其实自定义数组就可以解决问题缓冲区问题并提高效率。
         * 为什么还要使用流中的缓冲区对象呢?因为缓冲区对象中除了封装数组以外,
         * 还提供了更多的操作缓冲区数据的方法。
         * BufferedReader  BufferedWriter
         * 
         * 讲解字符流缓冲区中的特有方法。
         * 操作字符数据时,有一个文本特有的表形实行 :行(hang)
         * 操作行的方法。
         * BufferedReader:readLine():一次读取一行。
         * BufferedWriter:
         */
        
        copyTextByBuffer();
//      readText();
        
//      writeText();
    }

    public static void writeText() throws IOException {
        BufferedWriter bufw = new BufferedWriter(new FileWriter("tempfile/test24_buf.txt"));
        
        for(int x=1; x<=4; x++){
            bufw.write(x+"-itcast");
            bufw.newLine();
            bufw.flush();
        }
        bufw.close();
    }

    public static void readText() throws IOException {
        
        BufferedReader bufr = new BufferedReader(new FileReader("Test24.java"));
        
        
        String line = null;
        while((line=bufr.readLine())!=null){
            
            System.out.println(line);
            
        }
        
//      String line = bufr.readLine();
//      System.out.println("-"+line+"-");
//      String line1 = bufr.readLine();
//      System.out.println("-"+line1+"-");
        
        bufr.close();
        
        
    }

    public static void copyTextByBuffer() throws IOException {
        
        BufferedReader bufr = new BufferedReader(new FileReader("Test24.java"));
        BufferedWriter bufw = new BufferedWriter(new FileWriter("tempfile/test24_bufcopy.txt"));
        
        //循环读写一行数据。
        String line = null;
        while((line=bufr.readLine())!=null){
            bufw.write(line);
            bufw.newLine();
            bufw.flush();
        }
        
        bufw.close();
        bufr.close();   
    }
}

字符流缓冲区的原理图解

Java-IO流(五)-字符流(OutputStreamWriter、InputStreamReader)_第2张图片
字符流缓冲区的原理

字符流缓冲区read()方法和readLine()方法的实现

import java.io.IOException;
import java.io.Reader;

/**
 * 自定义一个字符流缓冲区。 用于缓冲字符数据,从而提高操作效率。 
 * 并提供了更多操作缓冲区数据的方法。 需要使用具体的流对象来完成数据的获取。
 * 
 * 分析: 缓冲区应该具备什么? 1,必须要有数组。 2,需要对数组进行操作,对数组操作一定要有角标。
 * 
 * @author Teaching
 * 
 */
public class MyBufferedReader {

    private Reader r;

    // 定义一个字符数组,作为缓冲区。
    private char[] buf = new char[1024];
    // 定义了一个索引,用于操作数组中的元素。
    private int index = 0;
    // 定义了一个变量,用于记录读取字符的个数。
    private int count = 0;

    // 需要一初始化就具备一个流对象。
    public MyBufferedReader(Reader r) {// 可以对Reader的所有子类进行高效读取。
        this.r = r;
    }

    /**
     * 提供一个可以从缓冲区中读取一个字符的方法。
     * 高效方法。
     * @throws IOException 
     * 
     */
    public int read() throws IOException {

        /*
         * 1,需要先通过流对象从底层设备上获取一定数据的数据到缓冲区数组中。 使用流对象read(char[]);
         */
        //如果count记录字符个数的变量为0,说明缓冲区已经没有字符数据。
        if(count==0){
            //需要从设备上获取一定数量的数据存储到缓冲区中,并用count记录存储字符的个数。
            count = r.read(buf);
            //每取一次新的数据,就需要将角标归0.
            index = 0;
        }
        //如果count小于0,说明到-1,没有数据了,程序直接返回-1.
        if(count<0){
            return -1;
        }
        //从缓冲区中取出一个字符。
        char ch = buf[index];
        //角标自增。
        index ++;
        //计数器要自减。
        count --;
        
        return ch;
    }
    
    /**
     *  基于高效的read方法,建立一个一次可以读取一行的数据的方法。
     *  将行终止符前的数据转成字符串返回。
     * @return
     * @throws IOException 
     */
    public String readLine() throws IOException{
        
        /*
         * 思路;
         * 
         * 从缓冲区中一次获取一个字符,并将这个字符存储到临时容器中。
         * 每获取一个字符都要进行判断,只要不是行终止符都进行存储。
         * 一旦读取到行终止符,就将临时容器中的数据转成字符串返回。
         * 
         */
        //1,定义一个临时容器。
        StringBuilder sb = new StringBuilder();
        
        //2,调用本类中的read方法,从缓冲区中读取一个字符,存储到临时容器中。
        //存的时候要注意:必须判断,如果是行终止符就不要存储了。就将临时容器中的
        //字符转成字符串返回。
        
        int ch = 0;
        while((ch=this.read())!=-1){
            if(ch=='\r'){
                continue;
            }
            
            if(ch=='\n'){
                return sb.toString();
            }
            
            sb.append((char)ch);//将读取到的字符数字转成char类型,存储到sb中。
            
        }
        
        //万一文本中最后以后没有行终止符,判断一下sb中是否有内容,如果有则返回。
        if(sb.length()!=0){
            return sb.toString();
        }
        
        
        return null;
        
    }
    

    // 关闭流资源。
    public void close() throws IOException {
        // 其实内部就是关闭具体的流。
        r.close();
    }

}

你可能感兴趣的:(Java-IO流(五)-字符流(OutputStreamWriter、InputStreamReader))