IO流

一、IO流原理及流的分类

IO流的原理

  1. I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。
  2. Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行。
  3. java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
  4. 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
  5. 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

 流的分类:

  • 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流
抽象基类字节流字符流
输入流 InputStream Reader
输出流 OutputStream Writer

 

  1. Java的IO流共涉及40多个类,实际上非常规则,都是从以上4个抽象基类派生的。
  2. 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

IO 流体系

二、 节点流(或文件流)

字符流:

 FileReader读入数据的基本操作

读取文件的步骤:

1、实例化File类的对象,指明要操作的文件

File file = new File("hello.txt");

2、FileReader流的实例化

FileReader fileReader = new FileReader(file);

3、读入数据的操作

char[] ch = new char[1024];
fileReader.read(ch);

4、关闭资源

fileReader.close();

FileReader读取数据操作: 

方式一:

     @Test
     public void FileReaderTest() throws IOException {
          //1、实例化File类的对象,指明要操作的文件
          File file = new File("hello.txt");
          //2、提供具体的流
          FileReader fr = new FileReader(file);
          //3、数据的读入
          //redd():返回读入的一个字符。如果到达文件末尾。范湖-1
          //方式一
          int date = fr.read();
          while (date != -1) {
               System.out.print((char)date);
               date = fr.read();
          }
          //4、流的关闭操作
          fr.close();
     }

方式二:语法上针对方式一的修改

     @Test
     public void FileReaderTest(){
          FileReader fr = null;
          //1、实例化File类的对象,指明要操作的文件
          File file = new File("hello.txt");
          //2、提供具体的流
          try {
               fr = new FileReader(file);
               //3、数据的读入
               //redd():返回读入的一个字符。如果到达文件末尾。范湖-1
               //方式一
               int date;
               while ((date = fr.read())!= -1) {
                    System.out.print((char)date);
               }
          } catch (IOException e) {
               e.printStackTrace();
          } finally {
               //4、流的关闭操作
               if (fr!=null){
                    try {
                         fr.close();
                    } catch (IOException e) {
                         e.printStackTrace();
                    }
               }

          }
     }

 FileRead中使用read(char[] cbuf)读入数据:

     @Test
     public void test2(){
         FileReader fr = null;
         File file = new File("hello.txt");
          try {
               fr = new FileReader(file);
               char[] cbuf = new char[5];
               int len;
               while ((len = fr.read(cbuf))!=-1){
                    //方式一:
                    //错误的写法
//                    for (int i = 0;i < cbuf.length;i++){
//                         System.out.print(cbuf[i]);
//                    }
                    //正确的写法
//                    for (int i = 0;i < len;i++){
//                         System.out.print(cbuf[i]);
//                    }
                    //方式二:
                    //错误的写法,对应着方式一的错误写法
//                    String str = new String(cbuf);
//                    System.out.print(str);
                    String str = new String(cbuf,0,len);
                    System.out.print(str);
               }
          } catch (IOException e) {
               e.printStackTrace();
          } finally {
               if (fr != null){
                    try {
                         fr.close();
                    } catch (IOException e) {
                         e.printStackTrace();
                    }
               }
          }
     }

FileWriter写出数据的操作

写入文件的步骤

1、实例化File类的对象,指明写出到的文件

File file = new File("hi.txt");

2、提供FileWriter的对象,用于数据的写出

FileWriter fw = new FileWriter(file);

3、写出的操作

fw.write("醉里挑灯看剑\n");
fw.write("梦回吹角连营\n");
fw.write("八百里分麾下炙\n");
fw.write("五十弦翻塞外声\n");
fw.write("沙场秋点兵\n");

4、流资源的关闭

fw.close();

 写出数据操作

    @Test
    public void test(){
        FileWriter fw = null;
        try {
            //1、提供File类的对象,指明写出到的文件
            File file = new File("hi.txt");
            //2、提供FileWriter流的对象,用于数据的写出
            fw = new FileWriter(file);
            //3、写出的操作
            fw.write("醉里挑灯看剑\n");
            fw.write("梦回吹角连营\n");
            fw.write("八百里分麾下炙\n");
            fw.write("五十弦翻塞外声\n");
            fw.write("沙场秋点兵\n");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //4、关闭流
                if (fw != null){
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

说明:

1、输出操作,对应的File可以不存在的。并不会报异常

2、File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。

File对应的硬盘中的文件如果存在:

  如果流使用的构造器是: FileWriter(file,false) / FileWriter(file):对原有

  如果流使用的构造器是: FileWriter(file, true):不会对原有文件覆盖,而是在原有文件基础上追加内容

使用FileReader和FileWriter实现文本文件的复制

 @Test
    public void test(){
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1、创建File类的对象,指明读入和写出的文件
            File file1 = new File("hello.txt");
            File file2 = new File("hi.txt");
            //2、创建输入流和输出流的对象
            fr = new FileReader(file1);
            fw = new FileWriter(file2);
            //3、数据的读入和写出操作
            char[] cbuf = new char[5];
            int len;//记录每次读入到cbuf数组中的祝福的个数
            while ((len = fr.read(cbuf)) != -1){
                //每次写入len个字符
                fw.write(cbuf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭流
            try {
                if (fr!=null)
                    fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fw!=null)
                    fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

字节流:

使用FileInputStream不能读取文本文件的测试

    @Test
    public void test(){
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1、创建File类的对象,指明读入和写出的文件
            File file1 = new File("JXBZ-139.jpg");
            File file2 = new File("JXBZ-140.jpg");
            //2、创建输入流和输出流的对象
            fr = new FileReader(file1);
            fw = new FileWriter(file2);
            //3、数据的读入和写出操作
            char[] cbuf = new char[5];
            int len;//记录每次读入到cbuf数组中的祝福的个数
            while ((len = fr.read(cbuf)) != -1){
                //每次写入len个字符
                fw.write(cbuf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭流
            try {
                if (fr!=null)
                    fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fw!=null)
                    fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

原图:

效果:

使用FileInputStream和FileOutputStream读写非文本文件

    @Test
    public void testFileInputStream(){
        FileInputStream fis = null;

        try {
            File file = new File("hello.txt");

            fis = new FileInputStream(file);

            byte[] buffer = new byte[5];
            int len;
            while ((len = fis.read(buffer))!=-1){
                System.out.print(new String(buffer,0,len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

结果:

 

原因:UTF-8中一个中文占三个字节的位置

结论:

1、对于文本文件(.txt,.java,.c,.cpp),使用字符流处理

2、对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),使用字节流处理

使用FileInputStream和FileOutputStream读写非文本文件

    @Test
    public void testFileInputOutputStream(){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //1、实例化File类指定要读取和写入的文件
            File srcFile = new File("JXBZ-139.jpg");
            File destFile = new File("JXBZ-140.jpg");
            //2、创建FileInputStream流和FileOutputStream流
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);
            //3、对数据进行读写
            byte[] bytes = new byte[1024];
            int len;
            while ((len = fis.read(bytes))!=-1){
                fos.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、关闭流
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

结果:

缓冲流   

为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。

缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:

  • BufferedInputStream和BufferedOutputStream
  • BufferedReader和BufferedWriter

当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区

当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。

向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法flush()可以强制将缓冲区的内容全部写入输出流

关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流

flush()方法的使用:手动将buffer中内容写入文件

如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出。

缓冲流(字节型)实现非文本文件的复制

    @Test
    public void test(){
        File file1;
        File file2;

        FileInputStream fis;
        FileOutputStream fos;

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1、造文件
            file1 = new File("D:\\Test\\最终效果.mov");
            file2 = new File("声之形.mov");
            //2、造流
            //2.1造节点流
            fis = new FileInputStream(file1);
            fos = new FileOutputStream(file2);
            //2.2造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            //3、复制的细节,读取,写入
            byte[] bytes = new byte[1024];
            int len;
            while ((len = bis.read(bytes)) != -1){
                bos.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            try {
                if (bis!=null)
                    bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bos!=null)
                    bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

缓冲流与字节流读写速度的对比:

缓冲流复制文件:

    @Test
    public void test(){
        long start = System.currentTimeMillis();
        File file1;
        File file2;

        FileInputStream fis;
        FileOutputStream fos;

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1、造文件
            file1 = new File("D:\\Test\\最终效果.mov");
            file2 = new File("声之形.mov");
            //2、造流
            //2.1造节点流
            fis = new FileInputStream(file1);
            fos = new FileOutputStream(file2);
            //2.2造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            //3、复制的细节,读取,写入
            byte[] bytes = new byte[1024];
            int len;
            while ((len = bis.read(bytes)) != -1){
                bos.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            try {
                if (bis!=null)
                    bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bos!=null)
                    bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("花费时间为"+(end-start)+"毫秒");
    }

节点流复制文件:

@Test
    public void test1(){
        long start = System.currentTimeMillis();
        File file1;
        File file2;

        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            //1、造文件
            file1 = new File("D:\\Test\\最终效果.mov");
            file2 = new File("声之形.mov");
            //2、造流
            //2.1造节点流
            fis = new FileInputStream(file1);
            fos = new FileOutputStream(file2);
            //3、复制的细节,读取,写入
            byte[] bytes = new byte[1024];
            int len;
            while ((len = fis.read(bytes)) != -1){
                fos.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            try {
                if (fis!=null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fos!=null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("花费时间为"+(end-start)+"毫秒");
    }

缓冲流的复制用时:

字节流复制用时:

缓冲流(字符流)实现文本文件的复制

 

    @Test
    public void test(){
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            //创建文件对应的流
            br = new BufferedReader(new FileReader(new File("hello.txt")));
            bw = new BufferedWriter(new FileWriter(new File("hi.txt")));
            //读写操作
            //方式一:
            //每次读入至多指定数组的大小
//            char[] cbuf = new char[1024];
//            int len;
//            while ((len = br.read(cbuf)) != -1){
//                bw.write(cbuf,0,len);
//            }
            //方式二:
            //使用String
            //每次读入一行
            String data;
            while ((data = br.readLine())!=null){
                //方法一:
//                bw.write(data+"\n");//data中不包含换行符
                //方法二:
                bw.write(data);
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭流
            try {
                if (br != null)
                    br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bw != null)
                    bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

转换流

  • 转换流提供了在字节流和字符流之间的转换
    • Java API提供了两个转换流:
      • InputStreamReader:将InputStream转换为Reader
      • 实现将字节的输入流按指定字符集转换为字符的输入流。
      • 需要和InputStream“套接”。
      • 构造器
        • public InputStreamReader(InputStreamin)
        • public InputSreamReader(InputStreamin,StringcharsetName)
        • 如:Reader isr= new InputStreamReader(System.in,”gbk”);
      • OutputStreamWriter:将Writer转换为OutputStream
      • 实现将字符的输出流按指定字符集转换为字节的输出流。
      • 需要和OutputStream“套接”。
      • 构造器
        • public OutputStreamWriter(OutputStreamout)
        • public OutputSreamWriter(OutputStreamout,StringcharsetName)
  • 字节流中的数据都是字符时,转成字符流操作更高效。
  • 很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。

转换流

 

 
 
posted @ 2022-11-14 16:28  羽十六  阅读(65)  评论(0)    收藏  举报