IO流随笔

IO流

在java程序中,数据的输入、输出操作以“流”的方式进行

数据源:data source 提供原始数据的原始媒介。常见的:数据库、文件、其他程序、内存、网络连接、IO设备。

流的分类

流的方向:

  1. 输入流:数据源到程序(InputStream、Reader读进来)

  2. 输出流:程序到目的地(OutputStream、Writer写出去)

处理数据单元:

  1. 字节流:按照字节读取数据(InputStream、OutputStream)

  2. 字符流:按照字符读取数据(Reader、Writer)

功能不同:

  1. 节点流:可以直接从数据源或目的地读写数据。

  2. 处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的主要是简化操作和提高性能。

节点流和处理流的关系:

  1. 节点流处于IO操作的第一线,所有操作必须通过他们进行

  2. 处理流可以对其他流进行处理(提高效率或操作灵活性)

字节流:InputStream 字节输出流的顶级类 OutputStream 字节输出流的顶级类

字符流: Writer 字符输入流的顶级类 Reader 字符输出流的顶级类

复制文件:

/**
* 功能:文件复制
* FileInputStrem   FileOutputStrem
*
* 此法缺点:
* 中转站太小,此法复制大文件时,效率比较低下,
*/
public class TestCopy1 {

   public static void main(String[] args) throws IOException {
       //创建一个输入和输入流
  //     File file = new File("d:/BugReport.txt");
       InputStream is = new FileInputStream(new File("d:/BugReport.txt"));

       OutputStream os = new FileOutputStream(new File("d:\\BugReport2.txt"));

       //使用输入流和输出流完成文件复制
       int n;    //中转站 ,比较小
       //读一个字节
    //   n = is.read(); //从输入流读取一个字节赋值给n
       while ((n= is.read()) != -1){  //没有读完
           //写一个字节
           os.write(n);
           //接着读下一个字节
       //   n = is.read();
      }
       //关闭流
       is.close();
       os.close();
  }
}

此方法缺点:当复制大文件时,复制效率低

改进:

public class TestCopy2 {

   public static void main(String[] args) throws IOException {
       //创建一个输入和输出流
       InputStream fis = new FileInputStream("d:/JDK1.6 API帮助文档.CHM");
       OutputStream fos = new FileOutputStream("d:/JDK1.6 API帮助文档2.CHM");

       //使用输入流和输出流复制文件
       //设置中转站
       byte[] bytes = new byte[1024];
       //读一次字节
       int len = 0;   //将源文件的内容读取到bytes中,返回真实读取到的字节数
       while ((len=fis.read(bytes)) != -1){   //判断没有读完   -1表示已经读取完毕
           //写一次字节
          // fos.write(bytes);   //写出1024字节流的内容
           fos.write(bytes,0,len);  // 写出0到len字节的内容
      }
       //关闭流
       fis.close();
       fos.close();
  }
}

字符流复制文件:

public class TestCopyChar1 {
   public static void main(String[] args) throws IOException {
       //创建一个输入和输入流
       Reader fr = new FileReader(new File("d:\\BugReport.txt"));
       Writer fw = new FileWriter(new File("d:\\BugRepor2.txt"));

       //定义一个中转站
       char[] chars = new char[1024];
       int len = 0;
       while ((len = fr.read(chars)) != -1){  ////判断有没有读取完,,返回-1表示已经读取完毕
           //写一次字符
           fw.write(chars,0,len);
      }
       //关闭流
       fr.close();
       fw.close();
  }
}

上面几个示例中都是将异常抛出,接下来修改一下例子,将异常捕获并进行处理:


public class TestCopyChar2 {

   public static void main(String[] args){

       //创建字符输入流和输出流
       Reader fr = null;
       Writer fw = null;
       try {
           fr = new FileReader(new File("d:/JDK1.6 API帮助文档.CHM"));
           fw = new FileWriter(new File("d:/JDK1.6 API帮助文档2.CHM"));

           //使用输入流和输出流复制文件
           //定义一个中转站
           char[] chars = new char[1024];
           int len;
           while ((len = fr.read(chars))!=-1){  //判断有没有读取完,,返回-1表示已经读取完毕
               //写出一次字符
               fw.write(chars,0,len);
          }
      } catch (FileNotFoundException e) {
           e.printStackTrace();
      } 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();
          }
      }
  }
}

 

字符流复制文件时,出现乱码问题。解决办法,先将源文件调整为和Eclpise一样的编码

注意

  1. 字节流可以读写任何文件(文本文件、二进制文件(音频视频文件 chm)),字符流只可以读写文本文件(word不是文本文件),但是字符流处理非英文字符文本非常方便。

  2. 其实只有字节流,没有字符流,字符流底层使用的还是字节流,Java在字节流基础上提供了字符流,给编程带来了便利。

  3. 字符流如何识别是英文字符还是中文字符 。

    • 英文占一个字节 最高位是0 0111 0011

    • 中文占两个字节 最高位是1 1011 1011 1001 1101



使用缓冲字节流复制文件 

使用缓冲流之前,中转站小,只有一个字节,所以每读一个字节,都要访问硬盘一次;每写一个字节,都要访问硬盘一次。

使用缓冲流后,先读缓冲区,如果缓冲区有,不需要读硬盘,若缓冲区没有,则一次性读取硬盘8192个字节,大大减少了访问硬盘的次数,提高了速度;写数据时,先写入缓冲区,等缓冲区满后(也可以调用flush()方法强制将缓冲区的内容全部写出),一次性写入到硬盘中,减少了访问硬盘的次数,提高了速度。

public class TestBuffCopy {

   public static void main(String[] args) {
       //创建一个输入和输出流
       BufferedInputStream bis = null;
       BufferedOutputStream bos = null;
       try {
           bis = new BufferedInputStream(new FileInputStream(new File("d:/JDK1.6 API帮助文档.CHM")));   // 输入缓冲区默认8192字节
           bos = new BufferedOutputStream(new FileOutputStream(new File("d:/JDK1.6 API帮助文档2.CHM")));   // 输出缓冲区默认8192字节
           //读取字节
           int n = 0;
           while ((n = bis.read()) != -1){
               //写出字节
               bos.write(n);
          }
      } catch (FileNotFoundException e) {
           e.printStackTrace();
      } catch (IOException e) {
           e.printStackTrace();
      }finally {
           try {
               if (bis != null){
                   bis.close();
              }
          } catch (IOException e) {
               e.printStackTrace();
          }
           try {
               if (bos != null){
                   bos.close();
              }
          } catch (IOException e) {
               e.printStackTrace();
          }
      }

 

 

使用缓冲字符流按行复制文件:

/**
* 只有文本文件才有行的概念
*/
public class TestBuffCopy2 {

   public static void main(String[] args) throws IOException {

       //创建输入流和输出流
       BufferedReader br = new BufferedReader(new FileReader(new File("d:/BugReport.txt")));
       BufferedWriter bw = new BufferedWriter(new FileWriter(new File("d:/BugReport2.txt")));

       //读一行
       String str = null;
       while ((str = br.readLine()) != null){
           //写一行
           bw.write(str);
           //写完一行就换行
           bw.newLine();
      }
       //关闭流
       br.close();
       bw.close();
  }
}

使用处理流:

  1. 提高性能

  2. 简化操作

     

使用转换流将键盘数据按行复制到文件:

public class TestBuffCopy3 {
   public static void main(String[] args) throws IOException {
       //键盘输入数据
       InputStream inputStream = System.in;
       //使用转换流,将字节流转换成字符流
       Reader reader = new InputStreamReader(inputStream);
       //字符输入流
       BufferedReader br = new BufferedReader(reader);
       //字符输出流
       BufferedWriter bw = new BufferedWriter(new FileWriter(new File("d:/java.txt")));


       //读一行字符
       String str = br.readLine();
       while (!"bye".equals(str)){ //判断当读取到bye时结束
           //写一行字符
           bw.write(str);
           //写完一行就换行
           bw.newLine();
           //读取下一行
           str = br.readLine();
      }
       //关闭流
       br.close();
       bw.close();
  }
}

使用打印流按行写入文件:

/**
* 打印流,只有输出流
*/
public class TestPrintWriter {

   public static void main(String[] args) throws FileNotFoundException {
       //打印输出流
       PrintWriter pw = new PrintWriter("d:/BugReport.txt");
       pw.println(23);
       pw.println('a');
       pw.println(3.14);
       pw.println(true);
       pw.println("dasd");

       //关闭流
       pw.close();
  }
}

System.out是PrintStream的一个实例:

public class TestPrintStream {

   public static void main(String[] args) throws FileNotFoundException {

       //System.out是PrintStream的一个实例
       PrintStream ps = System.out;
       ps.println(2);
       ps.println('a');
       ps.println(3.14);
       ps.println(true);
       ps.println("skjdh");

       //关闭流
       ps.close();
  }
}

执行main方法,在控制台打印出对应数据。

PrintStream类的Println()方法功能强大,可以将各种数据类型(基本数据类型、引用数据类型)直接写入到文件中,并且换行。不管什么类型,写入到文件中全部变成字符串。

缺点:

  1. 需要使用特殊的字符来区分各个内容,防止混淆 2#a#3.14#true#skjdh

  2. 读出来之后都是字符串,还需要将字符串转换成真实类型 "2" "a" "3.14" "true"

解决方案,使用数据流和对象流:

  1. DataInputStream 和 DataOutputStream 数据流(可以对各种基本数据类型和字符串直接读写)

  2. ObjectInputStream 和 ObjectOutputStream 对象流(可以对各种基本数据类型和引用数据类型直接读写)

使用数据流读写文件:

public class TestDataStream {

   public static void main(String[] args) throws IOException {
       writer();
    //   read();
  }

   /**
    * 写入的是二进制数据,用户不可直接读
    */
   public static void writer() throws IOException {
       //创建输出流
       OutputStream os = new FileOutputStream("d:/BugReport3.txt");
       BufferedOutputStream bos = new BufferedOutputStream(os);
       DataOutputStream dos = new DataOutputStream(bos);

       //写数据
       dos.writeInt(22);
       dos.writeChar('B');
       dos.writeBoolean(true);
       dos.writeDouble(3.14);
       dos.writeUTF("lrc");
       dos.writeUTF("hqn");

       //关闭流
       dos.close();
       bos.close();
       os.close();
  }

   /**
    * 用DataInputStream读数据
    */
   public static void read() throws IOException {
       //创建输出流
       InputStream is = new FileInputStream("d:/BugReport3.txt");
       BufferedInputStream bis = new BufferedInputStream(is);
       DataInputStream dis = new DataInputStream(bis);

       //写数据
       System.out.println(dis.readInt());
       System.out.println(dis.readChar());
       System.out.println(dis.readDouble());
       System.out.println(dis.readBoolean());
       System.out.println(dis.readUTF());

       //关闭流
       dis.close();
       bis.close();
       is.close();
  }
}

当需要对文件读写对象时,需要用对象流来进行操作。

使用对象流读写文件:

public class TestObject {

   public static void main(String[] args) throws IOException, ClassNotFoundException {
       writer();
    //   read();
  }

   /**
    * 写入的是二进制数据,用户不可直接读
    * @throws IOException
    */
   public static void writer() throws IOException {
       //创建输出流
       OutputStream os = new FileOutputStream("d:/BugReport3.txt");
       BufferedOutputStream bos = new BufferedOutputStream(os);
       ObjectOutputStream oos = new ObjectOutputStream(bos);

       //写数据
       oos.writeInt(22);
       oos.writeChar('B');
       oos.writeBoolean(true);
       oos.writeDouble(3.14);
       oos.writeUTF("lrc");
       oos.writeUTF("hqn");
       oos.writeObject(new Date());  //写一个Date对象

       //关闭流
       oos.close();
       bos.close();
       os.close();
  }

   /**
    * 用ObjectInputStream读数据
    */
   public static void read() throws IOException, ClassNotFoundException {
       //创建输出流
       InputStream is = new FileInputStream("d:/BugReport3.txt");
       BufferedInputStream bis = new BufferedInputStream(is);
       ObjectInputStream ois = new ObjectInputStream(bis);

       //写数据
       System.out.println(ois.readInt());
       System.out.println(ois.readChar());
       System.out.println(ois.readDouble());
       System.out.println(ois.readBoolean());
       System.out.println(ois.readUTF());
       System.out.println(ois.readUTF());
       Date date = (Date) ois.readObject(); //读出对象
       System.out.println(date);

       //关闭流
       ois.close();
       bis.close();
       is.close();
  }
}

复制文件夹

public class TestCopyDir {

   public static void main(String[] args) {
       //调用方法
       copyDir("e:/test","e:/test2");
  }

   /**
    * 复制文件夹
    */
   public static void copyDir(String sourceFile,String targetFile){

       //源文件必须存在
       File file = new File(sourceFile);
       if (!file.exists()){
           System.out.println("源文件不存在");
           return;
      }
       //创建目标文件
       File dir = new File(targetFile);
       if (!dir.exists()){
           //如果目标文件不存在,就创建
           dir.mkdirs();
      }
       //复制源文件的子文件夹到目标文件(不包含子文件)
       File[] files = file.listFiles();      //获得源文件下的所有文件和文件夹
       for (File f: files) {
           //如果是文件
           if (f.isFile()){
               //f.getName()获取该文件的名称
               copyFile(sourceFile+"/"+f.getName(),targetFile+"/"+f.getName());
          }
           //如果是文件夹
           if (f.isDirectory()){
               //递归
               copyDir(sourceFile+"/"+f.getName(),targetFile+"/"+f.getName());
          }
      }
  }

   /**
    * 复制文件
    * @param sourceFile 源文件
    * @param targetFile 目标文件
    */
   public static void copyFile(String sourceFile,String targetFile){

       BufferedInputStream bis = null;
       BufferedOutputStream bos = null;
       try {
           bis = new BufferedInputStream(new FileInputStream(new File(sourceFile)));
           bos = new BufferedOutputStream(new FileOutputStream(new File(targetFile)));

           //定义一个中转站
           byte[] bytes = new byte[1024];
           //读取字节
           int len = bis.read(bytes);
           while (len != -1){
               //写出字节
               bos.write(bytes,0,len);
               //继续读取字节
               len =  bis.read(bytes);
          }
      } catch (FileNotFoundException e) {
           e.printStackTrace();
      } catch (IOException e) {
           e.printStackTrace();
      } finally {
           //关闭输入流和输出流
           try {
               if (bis != null){
                   bis.close();
              }
          } catch (IOException e) {
               e.printStackTrace();
          }
           try {
               if (bis != null){
                   bos.close();
              }
          } catch (IOException e) {
               e.printStackTrace();
          }
      }

  }
}

 

 

 

 

 

 

posted @ 2021-05-28 00:14  清风揽月S  阅读(82)  评论(0)    收藏  举报