JAVA基础知识之InputStreamReader流

一、InputStreamReader类

    API文档说明:InputStreamReader类是从字节流到字符流的桥接器:它使用指定的字符集读取字节并将它们解码为字符。 它使用的字符集可以通过名称指定,也可以明确指定,或者可以接受平台的默认字符集。每次调用一个InputStreamReader的read()方法都可能导致从底层字节输入流中读取一个或多个字节。 为了实现字节到字符的有效转换,可以从基础流中提取比满足当前读取操作所需的更多字节。为了获得最高效率,请考虑在BufferedReader中包装InputStreamReader

   其继承Reader类

public class InputStreamReader extends Reader {}

1)字节流到字符流的桥梁怎么理解?

    1、计算机存储的单位是字节,如尽管txt文本中有中文汉字这样的字符,但是对计算机而言,其是字节形式存在的

    2、字节流读取是单字节读取,但是一个字符可能由1个或多个字节组成,所以字节读取会有问题,需要字符流读取

   3、 尽管我们使用字符流读取字符,但其底层本质还是字节流。因此就需要一个流,将底层的字节流封装成字符流

    4、InputStreamReader流就是起这个作用,把底层字节流封装成字符流进行字符读取工作,所以称为桥梁

2)使用指定的字符集读取字节并将它们解码为字符怎么理解?

      字节本质是8个二进制位,且不同的字符集对同一字节解码后的字符结果是不同的,因此在读取字符时务必要指定合适的字符集,否则读取的内容会产生乱码

3)它使用的字符集可以通过名称指定,也可以明确指定,或者可以接受平台的默认字符集怎么理解?

      意味着InputStreamReader类有多个方法或者多个构造方法来设置字符集

4)每次调用一个InputStreamReader的read()方法都可能导致从底层字节输入流中读取一个或多个字节怎么理解?

      因为字符流的read()方法是要返回一个字符的,但是不同的字符在字符集中可能由不同的字节个数组成,所以会导致这一结果

5)为了实现字节到字符的有效转换,可以从基础流中提取比满足当前读取操作所需的更多字节,考虑在BufferedReader中包装InputStreamReader

     该话暂未理解,需要了解BufferedReader类后对比读取效率才可得出答案

二、InputStreamReader构造方法

1)使用默认的字符集构造InputStreamReader流:本质是初始化其实例域的一个变量,并未看到任何关于字符集的设置

 public InputStreamReader(InputStream in) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null);
        } catch (UnsupportedEncodingException e) {
           
            throw new Error(e);
        }
    }

2)使用指定的字符集名称构造InputStreamReader流:本质是初始化其实例域的一个变量,可以发现字符集是初始化方法的第三个参数

 public InputStreamReader(InputStream in, String charsetName)
        throws UnsupportedEncodingException
    {
        super(in);
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
    }

3)sd变量:本质是StreamDecoder类的对象,InputStreamReader的构造方法就是在给此对象做初始化操作

private final StreamDecoder sd;

  三、InputStreamReaderAPI

 1、我们可以发现InputStreamReaderAPI类的所有API都是利用了sd变量的,因此可以看出InputStreamReader类的方法本质是调用StreamDecoder类方法

2、但是由于StreamDecoder类中的读取方法等涉及到了字节的高低位的等问题,所以暂时不去了解,待后续补充


    /**
     * 获取设置的字符集
     */
    public String getEncoding() {
        return sd.getEncoding();
    }

    /**
     * 读取流并返回一个字符,遇到文件末尾返回-1
     */
    public int read() throws IOException {
        return sd.read();
    }

    /**
     * 读取字符到字符数组的部分中,遇到文件末尾返回-1
     */
    public int read(char cbuf[], int offset, int length) throws IOException {
        return sd.read(cbuf, offset, length);
    }

    /**
     * 检测流是否准备好呗读取
     */
    public boolean ready() throws IOException {
        return sd.ready();
    }

    /**
    * 关闭流并释放资源
    */
    public void close() throws IOException {
        sd.close();
    }

四、InputStreamReader类的读取方法

1)读取一个或多个字节并返回一个字符,若遇到文件末尾,则返回-1:暂时不涉及具体细节,了解作用即可

 /**
     * 读取流并返回一个字符,遇到文件末尾返回-1
     */
    public int read() throws IOException {
        return sd.read();
    }

    read()方法实例:务必注意构造InputStreamReader对象时要指定字符集,否则会出现不可预估的错误

 utf-8的字符集解码,最终结果是:咕噜是个大胖子

 gbk的字符集解码,最终结果是:鍜曞櫆鏄釜澶ц儢瀛�--这样的就代表字符集错误,乱码了

 public static void main(String[] args)
    {
        // 建立文件对象,文中内容是"咕噜是个大胖子";
        File file = new File("C:\\Users\\Administrator\\Desktop\\4.txt");

        try
        {

            //构建InputStreamReader流对象并指定字符集
            InputStreamReader reader = new InputStreamReader(
                    new FileInputStream(file), "utf-8");

            int n = 0;
            
            StringBuffer buffer=new StringBuffer();

            while(n!=-1)
            {
                
                n=reader.read();
                
                char result=(char) n; //转为字符
                
                buffer.append(result);
                
            }
            System.out.println(buffer.toString());
        }

        catch (FileNotFoundException e)
        {

            System.out.println("文件不存在或者文件不可读或者文件是目录");
        }
        catch (IOException e)
        {
            System.out.println("读取过程存在异常");
        }
    }

2)读取最多length个字符到字符数组中,从数组的偏移量offset开始存储,读到文件末尾时返回-1     

public int read(char[] cbuf, int offset,int length) throws IOException

 使用该方法时务必要注意保证length要比读取的数据长度要大,否则读取的数据会出现乱序问题--原因要在具体实现中才能理解,暂时先记住即可

 当legnth为6时,结果为子噜是个大胖;当length为4时,结果为大胖子个;当length长度为8时,结果为咕噜是个大胖子

public static void main(String[] args)
    {
        // 建立文件对象,文中内容是"咕噜是个大胖子";字符个数是7个
        File file = new File("C:\\Users\\Administrator\\Desktop\\4.txt");

        try
        {

            // 构建InputStreamReader流对象
            InputStreamReader reader = new InputStreamReader(
                    new FileInputStream(file), "utf-8");

            int n = 0;
            
            char[] datas=new char[10]; 

    
            while (n != -1)
            {
              
                n = reader.read(datas,0,6); //length变量至关重要

            }
            
            reader.close();
            
            String resultString=new String(datas);
            
            System.out.println(resultString );

        }

        catch (FileNotFoundException e)
        {

            System.out.println("文件不存在或者文件不可读或者文件是目录");
        }
        catch (IOException e)
        {
            System.out.println("读取过程存在异常");
        }
    }

  如何解决该问题?即如何保证要读取的最大字符数是高于流的,因为字符是由1个或多个字节组成,因此我们可以利用字符的文件个数来做参,但是该步骤会导致字符数组内存浪费且结果的末尾会多很多空白

 char[] datas=new char[(int) file.length()];

五、InputStreamReader类与FileReader类关系

      1、FileReader类仅仅是InputStreamReader的简单衍生并未扩展任何功能

      2、FileReader类读取数据实质是InputStreamReader类在读取,而InputStreamReader读取数据实际是StreamDecoder类读取

      3、因此在使用字符输入流的时候实际是StreamDecoder类在发挥作用

  

猜你喜欢

转载自blog.csdn.net/ai_bao_zi/article/details/81133476