美文网首页
Java基础系列35-IO流

Java基础系列35-IO流

作者: 只是甲 | 来源:发表于2021-09-16 11:18 被阅读0次

    一.IO流概述

    什么是IO流,其作用为?

    1. I ------ Input --> 输入 --> 读取
    2. O — Output --> 输出 --> 写出

    常见应用:

    1. 文件复制
    2. 文件上传
    3. 文件下载

    IO流的分类汇总:

    1. 字节流 (支持图片等格式的文件,直接用记事本打开文件是看不懂的乱码
    2. 字节缓冲区流 (字符缓冲输入流 b、c方式 可以读取中文,不会出现乱码)
    3. 转换流 (字符流) = 字节流 + 编码表
    4. 字符流 (字符流数据通过Windows自带的记事本软件打开是可以读懂里面内容的)
    5. 字符缓冲区流
    • 字节流 (支持图片等格式的文件,直接用记事本打开文件是看不懂的乱码)
      a1 InputStream 字节流输入超类
        a1.1 FileInputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
        
    //指定输入流
    //FileInputStream fis = new FileInputStream(new File("fis.txt"));//效果同下行代码
    FileInputStream fis = new FileInputStream("fis.txt");
    
    //a.一次读取一个字符
      int by;
    //如果fis.read不等于-1说明还有数据,则继续读文件
      while ((by = fis.read()) != -1) {
        System.out.print((char) by);//打印结果
      }
    
      //b.一次读取一个字符数组
      byte[] bys = new byte[1024];//每次读取最大bys长度个字节存入数组,长度建议1024或1024的整数倍
      int len;//代表有效个数
      //将数据读取到数组中, 并用len记录读取到的有效字节个数
      //fis.read(bys)=>读取一次bys长度内容写入bys(每次覆盖之前内容),如果剩余文件内容不足以写满bys,(长度1024,剩余1000字节)则bys中最后字节(24字节)保持上一次写入的内容 => 所以每次要截取new String(bys,0,len),避免读到上一次剩余的内容
      while ((len = fis.read(bys)) != -1) {
        System.out.print(new String(bys, 0, len));//打印结果
      }
    
      //释放资源
      fis.close();
    
    

       b1 字节缓冲区流  BufferedInputStream 字符缓冲输入流 b、c方式 可以读取中文,不会出现乱码

      //字节流一次读写一个数组的速度比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想,所以提供了字节缓冲区流
      
      //Q:为什么字节缓冲流的构造方法需要传入一个In/OutputStream
      //A:字节缓冲区流仅仅提供缓冲区,而真正的底层的读写数据还得需要基本的流对象进行操作。
          
      //指定输入流
      BufferedInputStream bis = new BufferedInputStream(new FileInputStream("name.txt"));
      
      //a.一次读取一个字符(不能读取中文,中文一般2-4字节,一个一个字节读会乱码)
      int by;
      while ((by = bis.read()) != -1) {
          System.out.print((char) by);
      }
      
      //b.一次读取一个字符数组
      byte[] bys = new byte[1024];
      int len;
      while ((len = bis.read(bys)) != -1) {
          System.out.print(new String(bys, 0, len));
      }
      
      //释放资源
      bis.close();
    

    a2 OutPutStream 字节流输出超类
      a2.1 FileOutputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码

    //指定输出流
    //FileOutputStream fos = new FileOutputStream(new File("fos.txt"));//效果同下行代码
    //FileOutputStream fos = new FileOutputStream("fos.txt" , false);//false表示不追加文件->每次写入覆盖之前的内容
    FileOutputStream fos = new FileOutputStream("fos.txt");
    
    FileInputStream fis = new FileInputStream("fis.txt");
    
    //a.一次写入一个字符(不能读取中文,中文一般2-4字节,一个一个字节读会乱码)
    int by;
    while ((by = fis.read()) != -1) {
        System.out.print((char) by);//打印结果
    }
    
    //b.一次写入一个字符数组
    byte[] bytes = new byte[1024];
    int len;
    while ((len = fis.read(bytes)) != -1) {
        fos.write(bytes, 0, len);
    }
    
    //释放资源
    fos.close();
    fis.close();
    

       b2 字节缓冲区流  BufferedOutputStream 字节缓冲输出流 可以读取中文,不会出现乱码

    //字节流一次读写一个数组的速度比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想,所以提供了字节缓冲区流
    
    //Q:为什么字节缓冲流的构造方法需要传入一个In/OutputStream
    //A:字节缓冲区流仅仅提供缓冲区,而真正的底层的读写数据还得需要基本的流对象进行操作。
    
    //指定输入流
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
    
    //a.一次读取一个字符
    int by;
    while ((by = bis.read()) != -1) {
        bos.write(by);//写入字符串
        bos.flush();//刷新该流的缓冲,写入文件
    }
    
    //b.一次读取一个字符数组
    byte[] bytes = new byte[1024];
    int len;
    while ((len = bis.read(bytes)) != -1) {
        System.out.println(new String(bytes,0,len));
        bos.write(bytes,0,len);//写入字符串
        bos.flush();//刷新该流的缓冲,写入文件
    }
    
    //释放资源
    bis.close();
    bos.close();
    
    • c 转换流 (字符流) = 字节流 + 编码表
    //指定编码为UTF-8的字符输出流
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("source.txt"),"UTF-8");
    
    //指定编码为UTF-8的字符输入流
    InputStreamReader isr = new InputStreamReader(new FileInputStream("target.txt"),"UTF-8");
    
    /* 
    常见的编码表:
            ASCII : 美国标准信息交换码, 用一个字节的7位表示数据
            ISO-8859-1 : 欧洲码表, 用一个字节的8位表示数据, 兼容ASCII
            GB2312 : 中文码表的升级版, 融合了更多的中文文字符号, 兼容ASCII
            UTF-8 : 是一种可变长度的字符编码, 用1-3个字节表示数据, 又称为万国码, 兼容ASCII用在网页上可以
                    统一页面中的中文简体繁体和其他语言的显示.
    */
    
    • d字符流 (字符流数据通过Windows自带的记事本软件打开是可以读懂里面内容的)
      d1 Reader 字符流输入超类
        d1.1 InputStreamReader 用 默认/指定 编码读数据
    //指定输入流 目标文件和字符集 ,如果不指定字符集则按照默认编码表 (java7=GBK,java8=UTF-8)
    InputStreamReader isr = new InputStreamReader(new FileInputStream(""),"GBK");
    

       d1.1.1 FileReader ↓作为BufferedReader构造器的参数使用↓

    //转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流InputStreamReader提供了对应的子类InputStreamReader
    /*
      (子类)FileWriter:用来写入字符文件的便捷类
      (父类)OutputStreamWriter: FileWriter + 默认编码表(java7=GBK,java8=UTF-8)
     
      (子类)FileReader:用来读取字符文件的便捷类
      (父类)InputStreamReader: FileReader + 默认编码表(java7=GBK,java8=UTF-8)
    */
    
    //封装数据源
    FileReader fr = new FileReader("source.txt");
    //封装目的地
    FileWriter fw = new FileWriter("target.txt");
    

    e1 字符缓冲区流  BufferedReader 最常用

    //指定输入流
    BufferedReader br = new BufferedReader(new FileReader("source.txt"));
    //指定输出流
    BufferedWriter bw = new BufferedWriter(new FileWriter("target.txt"));
    
    //a.一次读取一个字符
    int ch;
    while ((ch = br.read()) != -1) {
        //对应Unicode编码10进制的值 查询工具 => 
        //http://www.mytju.com/classcode/tools/encode_gb2312.asp
        System.out.println(ch);
    }
    
    //b.一次读取多个字符【注意 换行符也算字符】
    char[] bytes = new char[1024];//一次取出1024个字符
    int len;//代表有效个数
    while((len = br.read(bytes)) != -1)
    {
        System.out.print(new String(chars, 0, len));//注意是print不是println
    }
    
    //c.读取一整行字符串【注意 不包括换行符,需要手动newLine()换行】
    //String line = br.readLine();
    String line;//代表一整行字符串
    //按行读取整篇文章的内容
    while((line = br.readLine()) != null)
    {
        System.out.println(line);
    }
    
    //释放资源
    br.close();
    bw.close();
    

      d2 Writer 字符流输出超类
       d2.1 PrintWriter
       d2.2 OutputStreamWriter 用 默认/指定 编码写数据

    //指定输出流 目标文件和字符集,如果不指定字符集则按照默认编码表 (java7=GBK,java8=UTF-8)
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("target.txt"),"GBK");
    

        d2.2.1 FileWriter ↓作为BufferedWriter构造器的参数使用↓

    //转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流OutputStreamWriter提供了对应的子类FileWriter
    /*
      (子类)FileWriter:用来写入字符文件的便捷类
      (父类)OutputStreamWriter: FileWriter + 默认编码表(java7=GBK,java8=UTF-8)
     
      (子类)FileReader:用来读取字符文件的便捷类
      (父类)InputStreamReader: FileReader + 默认编码表(java7=GBK,java8=UTF-8)
    */
    
    //封装数据源
    FileReader fr = new FileReader("source.txt");
    //封装目的地
    FileWriter fw = new FileWriter("target.txt");
    

       e2 字符缓冲区流 BufferedWriter 最常用

    //指定输入流
    BufferedReader br = new BufferedReader(new FileReader("br.txt"));
    //指定输出流
    BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
    
    //a.一次读取一个字符
    int ch;
    while ((ch = br.read()) != -1) {
        bw.write(ch);//写入字符串
        bw.flush();//刷新该流的缓冲,写入文件
    }
    
    //b.一次读取多个字符【注意 换行符也算字符】
    char[] bytes = new char[1024];//一次取出1024个字符
    int len;//代表有效个数
    while((len = br.read(bytes)) != -1)
    {
        bw.write(new String(bytes, 0 , len));//写入字符串
        bw.newLine();
        bw.flush();//刷新该流的缓冲,写入文件
    }
    
    //c.读取一整行字符串【注意 不包括换行符,需要手动newLine()换行】
    //String line = br.readLine();
    String line;//代表一整行字符串
    //按行读取整篇文章的内容
    while((line = br.readLine()) != null)
    {
        bw.write(line);//写入字符串
        bw.newLine();
        bw.flush();//刷新该流的缓冲,写入文件
    }
    
    //释放资源
    br.close();
    bw.close();
    

    二. 字节流

    a.字节流 (支持图片等格式的文件,直接用记事本打开文件是看不懂的乱码)
     InputStream 字节流输入超类
     FileInputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
      b. 字节缓冲区流  BufferedInputStream 字符缓冲输入流 b、c方式 可以读取中文,不会出现乱码
     OutPutStream 字节流输出超类
      FileOutputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
       b 字节缓冲区流  BufferedOutputStream 字节缓冲输出流 可以读取中文,不会出现乱码

    2.1 FOS与FIS

    简写说明:

    • FIS = FileInPutStream
    • FOS = FileOutPutStream

    字节流写数据:

    • OutputStream:此抽象类是表示输出字节流的所有类的超类
    • FileOutputStream:文件输出流是用于将数据写入 File

    字符流读数据:

    • OutputStream:此抽象类是表示输出字节流的所有类的超类
    • FileOutputStream:文件输出流是用于将数据写入 File

    构造方法:

    //创建一个向具有指定名称的文件中写入数据的输出文件流。
    FileOutputStream(String name)
    

    字节流写数据的步骤:

    1. 创建字节输出流对象
    2. 调用写数据的方法 write(int);
    3. 释放资源 close()

    代码:

    package Java_study;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-19
     * @remark  字节流FileOutputStream写出数据
     *
     */
    
    public class io1 {
        public static void main(String[] args) throws IOException {
            //创建直接输出流对象  throws FileNotFoundException
            FileOutputStream fos = new FileOutputStream("a.txt");
            
            //throws IOException (包含FileNotFoundException)
            System.out.println("fos.write");
            
            fos.write(65);//A
            fos.write(66);//B
            System.out.println("写入结束");
            //关闭IO流,回收系统资源
            fos.close();
        }
    
    }
    

    2.2 FOS写数据的三种方式

    方法摘要:

      //a.写出数据的三个方法
      //一次写一个字节
      public void write(int b)
      
      //一次写一个字节数组
      public void write(byte[] b)
      
      //一次写一个字节数组的一部分
      public void write(byte[] b,int off,int len)
      
      //b.String类中的方法
      //将字符串转换为字节数组
      byte[] getBytes()
    
    

    代码:

    package Java_study;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * 
     * @author 只是甲
     * @date   2021-07-19
     * @remark 字节流FileOutputStream写数据的三种方式
     * A.构造方法的三种方式:
     *      1. FileOutputStream(String name)
     *      2. FileOutPutStream(File file)
     *      3. FileOutPutStream(new File(xxx))
     *      4. FileOutputStream(xxx,true) //true=追加数据,默认为false,清空后重新写入数据
     *
     * B.写数据的三种方式: throws IOException
     *      1. FileOutputStream.write(int b):一次写一个字节
     *      2. FileOutputStream.write(byte[] b):一次写一个字节数组
     *      3. FileOutputStream.write(byte[] b,inf off,int len):一次写一个字节数组的一部分
     *
     * 字节流写数据的步骤:
     *      a: 创建字节输出流对象 new FileOutPutStream(String name)
     *      b: 调用写数据的方法
     *      c: 释放资源 fos.close()
     *
     */
    
    public class io2 {
        public static void main(String[] args) throws IOException{
            //A1. FileOutputStream构造方式1 FileOutputStream(String name)
            FileOutputStream fos1 = new FileOutputStream("b.txt");
            
            //A2. FileOutputStream构造方式2 FileOutPutStream(File file)
            File f2 = new File("c.txt");
            FileOutputStream fos2 = new FileOutputStream(f2);
            
            //A3. FileOutPutStream构造方式3 FileOutPutStream(new File(xxx))
            FileOutputStream fos3 = new FileOutputStream(new File("d.txt"));
            
            //B1. 写入数据方式1 FileOutputStream.write(int b):一次写一个字节
            //throws IOException
            fos1.write(65);
            
            //B2. 写入数据方式2 FileOutputStream.write(byte[] b):一次写一个字节数组
            byte[] bys = {65, 66, 67, 68, 69};
            fos2.write(bys);
            
            //B3. 写入数据方式3 FileOutputStream.write(byte[] b,inf off,int len):一次写一个字节数组的一部分
            fos3.write("ABCDE".getBytes(), 0, 3);
            
            File path = new File("");
            System.out.println("文件路径:" + path.getAbsolutePath());
            
            //释放资源
            fos1.close();
            fos2.close();
            fos3.close();
        }
    
    }
    

    2.3 FOS如何实现换行和追加写数据

    FIS = FileInPutStream

    不同的操作系统,针对换行的符号识别是不一样的:

    • windows:\r\n
    • linux:\n
    • mac:\r

    如何实现数据的追加写入?

    • 用构造方法带第二个参数是true的情况即可

    代码:

    package Java_study;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * 
     * @author 只是甲
     * @date   2021-07-19
     * @remark 字节流FileOutputStream写数据换行、追加
     * 
     */
    
    public class io3 {
        public static void main(String[] args) throws IOException{
            //先清空文件
            FileOutputStream clear = new FileOutputStream("b.txt");
            clear.write("".getBytes());
            
            //如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处(默认为false)
            FileOutputStream fos = new FileOutputStream("b.txt", true);
            
            //玄幻写入三次hello和换行
            for (int i = 0; i < 3; i++) {
                fos.write("hello".getBytes());
                fos.write("\r\n".getBytes());
            }
            
            System.out.println("文件路径为:" + new File("").getAbsolutePath());
            
            //释放资源
            fos.close();
        }
    
    }
    

    2.4 FOS写数据加入异常处理

    FIS = FileInPutStream

    try…catch.finally

      //格式
      try{
        可能发生问题的代码
      }catch(){
        处理异常代码
      }finally{
        一定会被执行的代码.   // 通常用于释放资源, 做善后的动作
      }
    

    代码:

    package Java_study;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * 
     * @author 只是甲
     * @date   2021-07-19
     * @remark 字节流FileOutputStream写数据加入异常处理
     * 
     */
    
    public class io4 {
        public static void main(String[] args) {
            //fos放在外面是为了在finally中可以关闭
            FileOutputStream fos = null;
            
            try {
                fos = new FileOutputStream("d.txt");
                fos.write("hello".getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    //释放资源
                    try {
                        fos.close();
                    } catch (IOException e) {
                        System.out.println("fos关闭失败");
                    }
                }
            }
        }
    
    }
    

    2.5 FIS读数据方式1一次读取一个字节

    FIS = FileInPutStream

    字节流读数据的步骤:

    • A : 创建字节输入流对象
    • B : 调用读数据的方法
    • C : 释放资源

    代码:

    package Java_study;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    
    /**
     * 
     * @author 只是甲
     * @remark 字节流FileInputStream读数据方式1
     * 
     */
    
    public class io5 {
        public static void main(String[] args) throws IOException {
            System.out.println("读取数据方式1: 一次读取一个字节");
            //创建字节输入流对象
            FileInputStream fis1 = new FileInputStream("b.txt");//刚才在b.txt写入了3行hello
            
            for (int i = 0; i < 20; i++) {
                System.out.println(fis1.read());
            }
            
            System.out.println("改进为循环方式打印");
            FileInputStream fis2 = new FileInputStream("b.txt");//刚才在b.txt写入了3行helloWorld
            int by;
            //如果fis.read不等于-1说明还有数据,则继续读文件
            while ((by = fis2.read()) != -1) {
                System.out.println((char) by);
            }
        }
    
    }
    

    测试记录:

    读取数据方式1: 一次读取一个字节
    104
    101
    108
    108
    111
    13
    10
    104
    101
    108
    108
    111
    13
    10
    104
    101
    108
    108
    111
    13
    改进为循环方式打印
    h
    e
    l
    l
    o
    
    
    
    
    h
    e
    l
    l
    o
    
    
    
    
    h
    e
    l
    l
    o
    
    
    
    
    

    2.6 FIS读数据方式2一次读取一个字节数组

    方法摘要:

      /*从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中
      返回值是读入缓冲区的字节总数,也就是实际的读取个数
      如果因为已经到达文件末尾而没有更多的数据,则返回 -1。*/
      public int read(byte[] b):
    

    代码:

    package Java_study;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    
    /**
     * 
     * @author 只是甲
     * @remark FileInputStream读数据方式2
     * 
     */
    
    
    public class io6 {
        public static void main(String[] args) throws IOException{
            System.out.println("----读取数据方式2: 一次读取一个直接数组-----");
            FileInputStream fis1 = new FileInputStream("b.txt");//刚才在b.txt写入了3行hello
            //标准代码字符缓冲区bys1长度应为1024或1024的整数倍,这里先用8演示读取的原理
            byte[] bys1 = new byte[8];
            //第一次读取
            int len1 = fis1.read(bys1);
            System.out.println(len1);//8
            
            System.out.println("第一次读取: " + new String(bys1));
            //第二次读取
            len1 = fis1.read(bys1);
            System.out.println(len1);//8
            /*
            第二次读取: ello
            he
            */
            System.out.println("第二次读取: " + new String(bys1));
            // 第三次读取
            len1 = fis1.read(bys1);
            System.out.println(len1);//5 => 只读取了5个字符,就返回了5
            
            /*
            第三次读取: llo
    
            he
            */
            /* 解析:
              文件内容为
              hello\r\n
              hello\r\n
              hello\r\n
    
              第一次读取8个字节为 [h] [e] [l] [l] [o] [\r][\n] [h]
              第二次读取8个字节为 [e] [l] [l] [o] [\r][\n] [h] [e]
              第三次读取8个字节为 [l] [l] [o] [\r][\n]
              但数组写入方式是每次覆盖前一次的,所以第二次写入的还保留着
              bys[5] = \r
              bys[6] = h
              bys[7] = e
              所以第三次数组内容为  [l] [l] [o] [\r][\n][\n] [h] [e]
              所以才会打印出以上内容
             */
            System.out.println("第三次读取: " + new String(bys1));
            //第四次读取
            len1 = fis1.read(bys1);
            System.out.println(len1);//-1 => 表示没有数据了
            
            
            
            System.out.println("-----改进为循环读取文件-----");
            //创建字节流输入对象
            FileInputStream fis2 = new FileInputStream("b.txt");
            byte[] bys2 = new byte[1024];//1024或者1024的整数倍
            
            int len2;
            //将数据读取到数组中, 并用len记录读取到的有效字节个数
            while ((len2 = fis2.read(bys2)) != -1 ) {
                //只读取到len的长度,否则会出现
                System.out.print(new String(bys2, 0, len2));
            }
            System.out.println("\r\n读取结束");
            fis2.close();
    
        }
    
    }
    

    测试记录:

    ----读取数据方式2: 一次读取一个直接数组-----
    8
    第一次读取: hello
    h
    8
    第二次读取: ello
    he
    5
    第三次读取: llo
    
    he
    -1
    -----改进为循环读取文件-----
    hello
    hello
    hello
    
    读取结束
    

    2.7 字节流练习之复制文本文件

    需求:

    • 拷贝文本文件

    分析:

    • 第一步: 创建输入输出流对象关联数据源和数据目的
    • 第二步: 定义字节数组,为了提高效率
    • 第三步: 将数据通过while循环不断读取到字节数组中
    • 第四步: 将数据从字节数组中取出并写出
    • 第五步: 释放资源

    待拷贝的文本文件(d:\窗里窗外.txt):

    《窗里窗外》是林青霞近5年所写的46篇散文的结集,虽是旧作,但该书记录她19岁以《窗外》成名后多方面的人生经历,字字真实深刻。《窗里窗外》共分为六个章节:“戏”里说的是她的出道故事、拍戏的甘苦、对于作品的内心话;“亲”谈她的家人亲情;“友”则书写她与挚友的交往,细谈她与三毛、黄霑、张国荣、龙应台、琼瑶、徐克等人的往来互动;“趣”是她的生活记趣,有旅行见闻,也有她与影迷的邂逅;“缘”则书写她一生难忘的相遇,像是和记者的友谊,和季羡林的会面之缘;“悟”里记录了她对人生的体悟和感动,以及她向圣严法师求道的故事。此外,书中还完整收录了林青霞一些未公开的照片。
    第1节:人生小语(1)…
    

    代码:

    package Java_study;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    
    /**
     * 
     * @author 只是甲
     * @remark 直接流复习之复制文本文件
     * 
     */
    
    public class io7 {
        public static void main(String[] args) throws IOException{
            //封装数据源
            FileInputStream fis = new FileInputStream("d:" + File.separator + "窗里窗外.txt");
            //封装目的地(目标文件,追加true清空重写false)
            FileOutputStream fos = new FileOutputStream("d:" + File.separator + "杜兰特.txt", false);
            
            //a.一次写入一个字符
            int by;
            while ((by = fis.read()) != -1 ) {
                fos.write(by);
            }
            
            //b.一次写入一个字符数组
            byte[] bys = new byte[1024];
            int len;
            while ((len = fis.read(bys)) != -1 ) {
                fos.write(bys, 0, len);
            }
            
            //释放资源
            fos.close();
            fis.close();
        }
    
    }
    

    2.8 字节流练习之复制图片

    思路:

    • 同理2.7案例

    代码:

    package Java_study;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-20
     * @remark  直接流复习之复制文本文件
     * 
     */
    
    public class io8 {
        public static void main(String[] args) throws IOException{
            //封装数据源
            FileInputStream fis1 = new FileInputStream("d:" + File.separator + "字节流.bmp");
            FileInputStream fis2 = new FileInputStream("d:" + File.separator + "字节流.bmp");
            //封装目的地
            FileOutputStream fos1 = new FileOutputStream("d:" + File.separator + "copy1.bmp");
            FileOutputStream fos2 = new FileOutputStream("d:" + File.separator + "copy2.bmp");
            
            //读取数据
            int by;
            //方式1: 一次读取一个直接
            //如果fis.read不等于-1说明还有数据,则继续读文件
            while ((by = fis1.read()) != -1 ) {
                fos1.write(by);
            }
            
            //方式2: 一次读取一个字节数组
            byte[] bys = new byte[1024];
            int len;
            while ((len = fis2.read(bys)) != -1) {
                fos2.write(bys, 0, len);
            }
            
            //释放资源
            fis1.close();
            fis2.close();
            fos1.close();
            fos2.close();
        }
    
    }
    

    三. 字节缓冲区流

    a.字节流 (支持图片等格式的文件,直接用记事本打开文件是看不懂的乱码)

    • InputStream 字节流输入超类
    • FileInputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
        b. 字节缓冲区流  BufferedInputStream 字符缓冲输入流 b、c方式 可以读取中文,不会出现乱码
    • OutPutStream 字节流输出超类
        FileOutputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
          b 字节缓冲区流  BufferedOutputStream 字节缓冲输出流 可以读取中文,不会出现乱码

    3.1 概述

    作用:

    • 字节流一次读写一个数组的速度比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果
    • java本身在设计的时候,也考虑到了这样的设计思想,所以提供了字节缓冲区流

    字节缓冲流:

    • BufferedOutputStream : 字节缓冲输出流
    • BufferedInputStream : 字节缓冲输入流

    为什么字节缓冲流的构造方法需要传入一个OutputStream:

    • 字节缓冲区流仅仅提供缓冲区,而真正的底层的读写数据还得需要基本的流对象进行操作

    代码:

    package Java_study;
    
    import java.io.*;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-20
     * @remark  字节缓冲流
     * 
     */
    
    
    public class io9 {
        public static void main(String[] args) throws IOException{
            //指定字符缓冲输出流
    
            // FileOutputStream fos = new FileOutputStream("a.txt");
            // BufferedOutputStream bos = new BufferedOutputStream(fos);
            // 上面的两句等价于下面的这一句
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"));
            
            byte[] bytes = "hello".getBytes();
            bos.write(bytes);
            bos.close();
            
            //指定字符缓冲输入流
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
            //方式1:  一次读取一个字节
            int by;
            while ((by = bis.read()) != -1) {
                System.out.println((char) (by));
            }
            
            //方式2: 一次读取一个直接数组
            byte[] bys = new byte[1024];
            int len;
            while ((len = bis.read(bys)) != -1) {
                System.out.println(new String(bys, 0, len));
            }
            
            //释放资源
            bos.close();
            bis.close();
        }
    }
    

    3.2 四种方式复制图片效率测试

    方法摘要:

    //返回以毫秒为单位的当前时间。
    public static long currentTimeMillis()
    

    代码:

    package Java_study;
    
    import java.io.*;
    
    /**
     * @author GroupiesM
     * @date 2021/04/27
     * @introduction 字节流&字节缓冲区流四种方式复制AVI并测试效率 
     *
     * 源文件: d:\\复制视频.avi
     * 大小: 34904KB
     *
     * method1: 基本字节流,一次读取一个字节    => method1.avi => 198001毫秒
     * method2: 基本字节流,一次读写一个字节数组 => method2.avi => 278毫秒
     * method3: 缓冲字节流,一次读取一个字节    => method3.avi => 384毫秒
     * method4: 缓冲字节流,一次读写一个字节数组 => method4.avi => 190毫秒
     *
     * 结论: method4 > method2 > method3 > method1
     */
    public class io10 {
        public static void main(String[] args) throws IOException {
            //记录method1时间
            long start1 = System.currentTimeMillis();
            method1();//method1共耗时198001毫秒
            long end1 = System.currentTimeMillis();
            System.out.println("method1共耗时" + (end1 - start1) + "毫秒");
    
            //记录method2时间
            long start2 = System.currentTimeMillis();
            method2();//method2共耗时278毫秒
            long end2 = System.currentTimeMillis();
            System.out.println("method2共耗时" + (end2 - start2) + "毫秒");
    
            //记录method3时间
            long start3 = System.currentTimeMillis();
            method3();//method3共耗时384毫秒
            long end3 = System.currentTimeMillis();
            System.out.println("method3共耗时" + (end3 - start3) + "毫秒");
    
            //记录method4时间
            long start4 = System.currentTimeMillis();
            method4();//method4共耗时190毫秒
            long end4 = System.currentTimeMillis();
            System.out.println("method4共耗时" + (end4 - start4) + "毫秒");
        }
    
        //method1: 基本字节流,一次读取一个字节    => method1.avi
        private static void method1() throws IOException {
            //封装数据源
            FileInputStream fis = new FileInputStream("d:\\复制视频.avi");
            //封装目的地
            FileOutputStream fos = new FileOutputStream("d:\\method1.avi");
    
            int by;
            while ((by = fis.read()) != -1) {
                fos.write(by);
            }
            //释放资源
            fis.close();
            fos.close();
        }
    
        //method2: 基本字节流,一次读写一个字节数组 => method2.avi
        private static void method2() throws IOException {
            //封装数据源
            FileInputStream fis = new FileInputStream("d:\\复制视频.avi");
            //封装目的地
            FileOutputStream fos = new FileOutputStream("d:\\method2.avi");
    
            byte[] bys = new byte[1024];
            int len;
            while ((len = fis.read(bys)) != -1) {
                fos.write(bys, 0, len);
            }
            //释放资源
            fis.close();
            fos.close();
        }
    
        //method3: 缓冲字节流,一次读取一个字节    => method3.avi
        private static void method3() throws IOException {
            //封装数据源
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制视频.avi"));
            //封装目的地
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\method3.avi"));
    
            int by;
            while ((by = bis.read()) != -1) {
                bos.write(by);
            }
            //释放资源
            bis.close();
            bos.close();
        }
    
        //method4: 缓冲字节流,一次读写一个字节数组 => method4.avi
        private static void method4() throws IOException {
            //封装数据源
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制视频.avi"));
            //封装目的地
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\method4.avi"));
    
            byte[] bys = new byte[1024];
            int len;
            while ((len = bis.read(bys)) != -1) {
                bos.write(bys, 0, len);
            }
            //释放资源
            bis.close();
            bos.close();
        }
    }
    

    四. 转换流

    c. 转换流 (字符流) = 字节流 + 编码表

    转换流出现的原因:

    • 由于字节流操作中文不是特别方便,所以,java就提供了转换流
    • 转换流 = 字节流 + 编码表

    字节流读数据可能出现问题:

    • 字节流一次读取一个字节的方式读取带有汉字的文件是有问题的,因为你读取到一个字节后就转为字符在控制台输出了,而汉字是由2个字节组成的,所以这里会出问题。
    • 文件复制的时候,字节流读取一个字节,写入一个字节,这个没有出现问题,是因为最终底层会根据字节做拼接,得到汉字。
    • 汉字存储的规则
        左边的字节数据肯定是负数,右边的字节数据可能是负数,也可能是正数,大部分情况下是负数。

    代码:

    package Java_study;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.Arrays;
    
    /**
     * @author 只是甲
     * @date   2021-07-20
     * @remark 转换流出现的原因
     *
     */
    
    public class io11 {
        public static void main(String[] args) throws IOException{
            //基本字节流一次读取一个字节
            FileInputStream fis = new FileInputStream("a.txt");
            
            int by;
            while ((by = fis.read()) != -1 ) {
                System.out.print((char) by);
            }
            //释放资源,刷新缓冲区
            fis.close();
            
            System.out.println("");
            String s = "你";
            byte[] bys = s.getBytes("GBK");
            /*常见的编码表测试 中文"你"转换为字节数组
            UTF-8          => [-28, -67, -96]  => JDK8中不指定编码则默认为 UTF-8
            GBK/GB2312     => [-60, -29]       => JDK7中不指定编码则默认为 GBK/GB2312
            ISO-8859-1     => [63]
            ASCII          => [63]
            UNICODE/UTF-16 => [-2, -1, 79, 96] => UNICODE 默认使用的是UTF-16的实现方式
         */
            
            System.out.println(Arrays.toString(bys));
        }
    
    }
    

    测试记录:

    hi
    ????
    [-60, -29]
    

    4.1 什么是编码表

    编码表

    • 由字符及其对应的数据组成的一张表
    ASCII
        ‘a’ 97
        ‘A’ 65
        ‘0’ 48
    

    常见的编码表:

    • ASCII : 美国标准信息交换码, 用一个字节的7位表示数据;(ASCII 0-126)
    • ISO-8859-1 : 欧洲码表, 用一个字节的8位表示数据, 兼容ASCII;
    • GB2312/GBK : 中文码表的升级版, 融合了更多的中文文字符号, 兼容ASCII;
    • UTF-8 : 使用一到四个字节来编码一个码点。, 兼容ASCII;用在网页上可以统一页面中的中文简体繁体和其他语言的显示;
    • UNICODE : 为世界上所有字符都分配了一个唯一的数字编号,又称万国码;有多种实现方案(UTF-32 、UTF-16 、UTF-8)。

    乱码问题:

    • 针对同一个数据, 采用的编码和解码不一致导致


      image.png

    代码:

    package Java_study;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  遍历编码表_int转char
     *
     */
    
    public class io12 {
        public static void main(String[] args) {
            int count = 0;
            for (int i = 8; i < 127; i++) {
                //每打印5个就换行一次
                if (count == 5) {
                    System.out.println("");
                    count = 0;
                }
                //13换行一次,否则CR归位键会回到行最左侧位置,导致控制台打印的10-12被吞掉
                if (i == 13) {
                    System.out.println("");
                }
                char ch = (char) i;
                System.out.print("[" + ch + " = " + i + "]\t");
                count++;
            }
        }
    
    }
    

    测试记录:

    [ = 8] [    = 9]   [
     = 10]  [� = 11]    [ = 12]    
    
    [
     = 13]  [� = 14]    [� = 15]    [� = 16]    [� = 17]    
    [� = 18]    [� = 19]    [� = 20]    [� = 21]    [� = 22]    
    [� = 23]    [� = 24]    [� = 25]    [� = 26]    [� = 27]    
    [� = 28]    [� = 29]    [� = 30]    [� = 31]    [  = 32]    
    [! = 33]    [" = 34]    [# = 35]    [$ = 36]    [% = 37]    
    [& = 38]    [' = 39]    [( = 40]    [) = 41]    [* = 42]    
    [+ = 43]    [, = 44]    [- = 45]    [. = 46]    [/ = 47]    
    [0 = 48]    [1 = 49]    [2 = 50]    [3 = 51]    [4 = 52]    
    [5 = 53]    [6 = 54]    [7 = 55]    [8 = 56]    [9 = 57]    
    [: = 58]    [; = 59]    [< = 60]    [= = 61]    [> = 62]    
    [? = 63]    [@ = 64]    [A = 65]    [B = 66]    [C = 67]    
    [D = 68]    [E = 69]    [F = 70]    [G = 71]    [H = 72]    
    [I = 73]    [J = 74]    [K = 75]    [L = 76]    [M = 77]    
    [N = 78]    [O = 79]    [P = 80]    [Q = 81]    [R = 82]    
    [S = 83]    [T = 84]    [U = 85]    [V = 86]    [W = 87]    
    [X = 88]    [Y = 89]    [Z = 90]    [[ = 91]    [\ = 92]    
    [] = 93]    [^ = 94]    [_ = 95]    [` = 96]    [a = 97]    
    [b = 98]    [c = 99]    [d = 100]   [e = 101]   [f = 102]   
    [g = 103]   [h = 104]   [i = 105]   [j = 106]   [k = 107]   
    [l = 108]   [m = 109]   [n = 110]   [o = 111]   [p = 112]   
    [q = 113]   [r = 114]   [s = 115]   [t = 116]   [u = 117]   
    [v = 118]   [w = 119]   [x = 120]   [y = 121]   [z = 122]   
    [{ = 123]   [| = 124]   [} = 125]   [~ = 126]   
    

    4.2 String类的编解码

    方法摘要:
    编码 : 把看得懂的变成看不懂的

    //使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。 
    public byte[] getBytes(String charsetName) throws UnsupportedEncodingException
    

    解码 : 把看不懂的变成看得懂的

    //通过使用指定的 charset解码指定的 byte 数组,构造一个新的 String。
    public String(byte[] bytes, String charsetName)
    

    重点强调: 编码和解码的方式需要一致

    常见的编码表测试 中文"你"转换为字节数组
    UTF-8 => [-28, -67, -96] => JDK8中不指定编码则默认为 UTF-8
    GBK/GB2312 => [-60, -29] => JDK7中不指定编码则默认为 GBK/GB2312
    ISO-8859-1 => [63]
    ASCII => [63]
    UNICODE/UTF-16 => [-2, -1, 79, 96] => UNICODE 默认使用的是UTF-16的实现方式

    代码:

    package Java_study;
    
    import java.io.UnsupportedEncodingException;
    import java.util.Arrays;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  String 类的编解码
     *
     */
    
    public class io13 {
        public static void main(String[] args) throws UnsupportedEncodingException {
            //定义一个字符串
            String s = "你好";
            
            //a.编码 getBytes => throws UnsupportedEncodingException 不支持的编码异常
            byte[] encodeGBK = s.getBytes();
            System.out.println(Arrays.toString(encodeGBK));//[-60, -29, -70, -61]
            
            byte[] encodeUTF = s.getBytes("UTF-8");
            System.out.println(Arrays.toString(encodeUTF));//[-28, -67, -96, -27, -91, -67]
            
            //b.解码
            byte[] bytesGBK = {-60, -29, -70, -61};
            String decodeGBK = new String(bytesGBK);
            //Java8默认编解码字符集为UTF8 用UTF8解码 GBK编码的字节码文件就会出现乱码
            System.out.println(decodeGBK);
            String decodeGBKAgain = new String(bytesGBK, "GBK");
            //指定用GBK解码
            System.out.println(decodeGBKAgain);//你好
            
            byte[] bytesUTF = {-28, -67, -96, -27, -91, -67};
            //不指定解码字符集,Java8环境默认使用UTF8字符集进行编解码,刚好和字节数组匹配
            String decodeUTF = new String(bytesUTF);
            System.out.println(decodeUTF);//你好
        }
    
    }
    

    测试记录:

    [-60, -29, -70, -61]
    [-28, -67, -96, -27, -91, -67]
    你好
    你好
    浣犲ソ
    

    4.3 字符流Stream的编解码(写入文件)

    OutputStreamWriter 字符输出流

     //根据【默认编码】把字节流的数据转换为字符流
     public OutputStreamWriter(OutputStream out)
    
     //根据【指定编码】把字节流数据转换为字符流
     public OutputStreamWriter(OutputStream out,String charsetName)
    

    InputStreamReader 字符输入流

     //用【默认编码】读数据 
     public InputStreamReader(InputStream in)
    
     //用【指定编码】读数据
     public InputStreamReader(InputStream in,String charsetName)
    

    IDEA设置文件编码


    image.png
    image.png

    代码:

    package Java_study;
    
    import java.io.*;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  String 类的编解码
     *
     */
    
    
    public class io14 {
        public static void main(String[] args) throws IOException{
            //指定输出流 目标文件和字符集
            OutputStreamWriter oswGBK = new OutputStreamWriter(new FileOutputStream("osw.txt"), "GBK");
            //指定输入流 目标文件和字符集
            InputStreamReader isrGBK = new InputStreamReader(new FileInputStream("osw.txt"), "GBK");
            InputStreamReader isrUTF = new InputStreamReader(new FileInputStream("osw.txt"), "utf-8");
            
            //调用写数据的方法
            oswGBK.write("你好");
            oswGBK.flush();//刷新缓冲流(执行一次写入)
            
            //用GBK解码GBK文本
            int byGBK;
            while ((byGBK = isrGBK.read()) != -1) {
                System.out.print((char) byGBK);//你好
            }
            
            System.out.println("");
            
            //用UTF-解码GBK文本
            int byUTF;
            while ((byUTF = isrUTF.read()) != -1) {
                System.out.print((char) byUTF);//乱码
            }
            
            //释放资源
            oswGBK.close();
            isrGBK.close();
            isrUTF.close();
        }
    
    }
    

    测试记录:

    你好
    ???
    

    五. 字符流

    image.png

    5.1 复制Java文件

    需求:
    把项目目录下的io1.java内容复制到项目目录下的Copy.java中

    三种方式:

    • InputStreamReader & OutputStreamWriter (字符流方式 见5.1) 379毫秒
    • FileReader & FileWriter (字符流方式 见5.1) 297毫秒
    • BufferedReader & BufferedWriter (字符缓冲区流方式 见6.2) 533毫秒

    代码:

    package Java_study;
    
    import java.io.*;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  字符流 复制Java文件
     *
     */
    
    public class io15 {
        public static void main(String[] args) throws IOException {
            long start1 = System.currentTimeMillis();
            for (int i = 0; i <  1000; i++) {
                method1("io1.java", "Copy.java");//method1共耗时2153毫秒
            }
            long end1 = System.currentTimeMillis();
            System.out.println("method1共耗时"+ (end1 - start1) + "毫秒");
            
            long start2 = System.currentTimeMillis();
            for (int i = 0; i < 1000; i++) {
                method1("io1.java", "Copy.java");
            }
            long end2 = System.currentTimeMillis();
            System.out.println("method1共耗时"+ (end2 - start2) + "毫秒");
        }
        
        public static void method1(String source, String target) throws IOException {
            //封装数据源
            InputStreamReader isr = new InputStreamReader(new FileInputStream(source));
            //封装目的地
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(target));
            
            char[] chArr = new char[1024];
            int len;
            while ((len = isr.read(chArr)) != -1) {
                osw.write(chArr, 0, len);
            }
            
            //释放资源
            osw.close();
            isr.close();
        }
        
        public static void method2(String source, String target) throws IOException{
            //封装数据源
            FileReader fr = new FileReader(source);
            //封装目的地
            FileWriter fw = new FileWriter(target);
            
            char[] chArr = new char[1024];
            int len;
            while ((len = fr.read(chArr)) != -1) {
                fw.write(chArr,0,len);
            }
            
            //释放资源
            fw.close();
            fr.close();
        }
    
    }
    

    测试记录:

    method1共耗时379毫秒
    method1共耗时297毫秒
    

    5.2 OutputStreamWriter写数据的6种方式

    方法摘要:

    /* @introduction 写一个字符
     * @param c 要写入的字符
     */
    public void write(char c)
        
    /* @introduction 写一个字符
     * @param c 要写入的字符对应码表中int值
     */
    public void write(int c)
    
    /* @introduction 写一个字符数组
     * @param cbuf 要写入的字符数组
     */
    public void write(char[] cbuf)
    
    /* @introduction 写一个字符数组的一部分
     * @param cbuf 要写入的字符数组
     * @param off 起始索引
     * @param len 从起始索引起,写入字符数组长度
     */    
    public void write(char[] cbuf,int off,int len)
        
    /* @introduction 写一个字符串
     * @param str 要写入的字符串
     */    
    public void write(String str)
        
    /* @introduction 写一个字符串的一部分
     * @param str 要写入的字符串
     * @param off 起始索引
     * @param len 从起始索引起,写入字符串长度
     */    
    public void write(String str,int off,int len)
    

    代码:

    package Java_study;
    
    import java.io.*;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  OutputStreamWriter写数据的6种方式
     *
     */
    
    public class io16 {
        public static void main(String[] args) throws IOException{
            //创建字符输出流对象 (同时创建文件)
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("target.txt"));
            
            //a. public void write(char c); 写一个字符
            methodA(osw);//写入一个字符a
            osw.write('\r');//换行
            
            //b. public void write(int c); 写一个字符
            methodB(osw);//写入一个字符b
            osw.write('\r');//换行
    
            //c. public void write(char[] cbuf); 写一个字符数组
            methodC(osw);//写入abcde
            osw.write('\r');//换行
    
            //d. public void write(char[] cbuf,int off,int len); 写一个字符数组的一部分
            methodD(osw);//写入c
            osw.write('\r');//换行
    
            //e. public void write(String str); 写一个字符串
            methodE(osw);//写入hello
            osw.write('\r');//换行
    
            //f. public void write(String str,int off,int len); 写一个字符串的一部分
            methodF(osw);//写入lo
            osw.write('\r');//换行
    
            //释放资源
            osw.close();//关闭此流,关闭前将缓冲区(内存)字符刷新(flush=>写入硬盘)
            
            
        }
        
        public static void methodA(OutputStreamWriter osw) throws IOException {
            osw.write('a');
        }
        
        public static void methodB(OutputStreamWriter osw) throws IOException {
            osw.write(98);
        }
        
        public static void methodC(OutputStreamWriter osw) throws IOException {
            char[] chs = {'a', 'b', 'c', 'd', 'e'};
            osw.write(chs);//写入 abcde
        }
        
        
        public static void methodD(OutputStreamWriter osw) throws IOException {
            char[] chs = {'a', 'b', 'c', 'd', 'e' };
             osw.write(chs, 2, 1);//写入bcd
        }
        
        public static void methodE(OutputStreamWriter osw) throws IOException {
            String s = "hello";
            //e. public void write(String str); 写一个字符串
            osw.write(s);//写入hello
            //void flush():刷新该流的缓冲
            //osw.flush();
        }
        
        public static void methodF(OutputStreamWriter osw) throws IOException {
            String s = "hello";
            //f. public void write(String str,int off,int len); 写一个字符串的一部分
            osw.write(s, 3, 2);
            //void flush():刷新该流的缓冲
            //osw.flush();
        }
        
    
    }
    

    5.3 InputStreamReader读数据的2种方式

    方法摘要

    //一次读取一个字符    
    public int read()
    
    //一次读取一个字符数组
    public int read(char[] cbuf)
    

    代码:

    package Java_study;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  InputStreamReader读数据的2种方式
     *
     */
    
    public class io17 {
        public static void main(String[] args) throws IOException{
            //创建字符流输入对象
            InputStreamReader isrA = new InputStreamReader(new FileInputStream("a.txt"));
            InputStreamReader isrB = new InputStreamReader(new FileInputStream("a.txt"));
            //a. public int read(); 一次读取一个字符
            methodA(isrA);
            System.out.println("\r\n==========================");
            //b. public int read(char[] cbuf); 一次读取一个字符数组
            methodB(isrB);
            
            //释放资源
            isrA.close();
            isrB.close();
        }
        
        
        public static void methodA(InputStreamReader isr) throws IOException{
            int ch;
            while ((ch = isr.read()) != -1) {
                System.out.print((char) ch);
            }
        }
        
        public static void methodB(InputStreamReader isr) throws IOException{
            char[] chs = new char[1024];
            int len;
            while ((len = isr.read(chs)) != -1) {
                System.out.println(new String(chs, 0, len));
            }
        }
    
    }
    

    六. 字符缓冲区流

    image.png

    6.1 概述

    BufferedWriter

    • 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
    • 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
    • 构造方法
    BufferedWriter(Writer out)
    

    BufferedReader

    • 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
    • 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
    • 构造方法
    BufferedReader(Reader in)
    

    代码:

    package Java_study;
    
    import java.io.*;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  字符缓冲区流概述
     *
     */
    
    public class io18 {
        public static void main(String[] args) throws IOException{
            //创建字符缓冲输出流对象
            BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
            //调用写数据的方法
            bw.write("hello");
            //释放资源
            bw.close();
            
            //创建字符缓冲输入流对象
            BufferedReader br1 = new BufferedReader(new FileReader("io1.java"));
            BufferedReader br2 = new BufferedReader(new FileReader("io1.java"));
            
            //方式1:一次读取一个字符
            method1(br1);
            //方式2:一次读取一个字符数组
            method2(br2);
            
            //释放资源
            br1.close();
            br2.close();
       }
        
        public static void method1(BufferedReader br) throws IOException{
            int ch;
            while((ch=br.read()) != -1) {
                System.out.print((char)ch);
            }
        }
        
        public static void method2(BufferedReader br) throws IOException{
            char[] chs = new char[1024];
            int len;
            while ((len = br.read(chs)) != -1) {
                System.out.print(new String(chs, 0, len));
            }
        }
    
    }
    

    6.2 复制文本文件

    需求:

    • 把项目目录下的a.txt内容复制到项目目录下的b.txt中
    • 数据源
       a.txt—读数据—字符流—InputStreamReader—FileReader—BufferedReader
    • 目的地
       b.txt—写数据—字符流—OutputStreamWriter—FileWriter—BufferedWriter

    代码:

    package Java_study;
    
    import java.io.*;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  字符缓冲区流 复制文本文件
     *
     */
    
    public class Io19 {
        public static void main(String[] args) throws IOException{
            //封装数据源
            BufferedReader br = new BufferedReader(new FileReader("a.txt"));
            //封装目的地
            BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
            
            //读写数据
            char[] chs = new char[1024];
            int len;
            while ((len = br.read(chs)) != -1 ) bw.write(chs, 0, len);
            
            //释放资源
            bw.close();
            br.close();
        }
    
    }
    

    6.3 复制Java文件

    需求:
    把项目目录下的io1.java内容复制到项目目录下的Copy.java中

    三种方式:

    • InputStreamReader & OutputStreamWriter (字符流方式 见5.1) 379毫秒
    • FileReader & FileWriter (字符流方式 见5.1) 297毫秒
    • BufferedReader & BufferedWriter (字符缓冲区流方式 见6.2) 533毫秒

    代码:

    package Java_study;
    
    import java.io.*;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  字符缓冲区流 字符缓冲区流复制Java文件
     *
     */
    
    public class Io20 {
        public static void main(String[] args) throws IOException {
            long start3 = System.currentTimeMillis();
            for (int i = 0; i < 1000; i++) {
                method3("io1.java", "Copy.java");
            }
            long end3 = System.currentTimeMillis();
            System.out.println("method3共耗时"+ (end3 - start3) + "毫秒");
        }
        
        public static void method3(String source,String target) throws IOException {
            //封装数据源
            BufferedReader br = new BufferedReader(new FileReader(source));
            //封装目的地
            BufferedWriter bw = new BufferedWriter(new FileWriter(target));
            //读写数据
            String line;
            while ((line = br.readLine()) != null) {
                bw.write(line);
                bw.newLine();
                bw.flush();
            }
            
            //释放资源
            bw.close();
        }
    
    }
    

    6.4 字符缓冲区流的特殊功能

    BufferedWriter

    //写入一个行分隔符,这个行分隔符是由系统决定的
    void newLine()
    

    BufferedReader

    //包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
    String readLine()
    

    代码:

    package Java_study;
    
    import java.io.*;
    
    /**
     * 
     * @author  只是甲
     * @date    2021-07-21
     * @remark  字符缓冲区流 字符缓冲区流复制Java文件
     *
     */
    
    public class Io21 {
        public static void main(String[] args) throws IOException{
            //创建字符缓冲输出流对象
            BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
            //写数据
            for (int x = 0; x < 3; x++) {
                bw.write("hello");
                bw.newLine();
                bw.flush();
            }
            
            //释放资源
            bw.close();
            
            //创建字符缓冲输入流对象
            BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
            
            //创建line对象接收每一行数据
            String line;
            //b. String readLine():包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
            
            //释放资源
            br.close();
        }
    
    }
    
    

    参考:

    1. https://blog.csdn.net/qq_43529621/article/details/116603510

    相关文章

      网友评论

          本文标题:Java基础系列35-IO流

          本文链接:https://www.haomeiwen.com/subject/pqtmpltx.html