Java中的IO流(二)

时间:2022-10-12 21:31:48

  上一篇《Java中的IO流(一)》把学习IO流的字符流作了一下记录,本篇把字节流记录一下。

一,Java中的字节流

  Java中的字节流的操作方式与字符流的操作方式大致相同,连方法名都是类似的,只是在具体的实现上会有稍许差别;

  字节流的两个顶层类:InputStream输入流即读和OutputStream输出流即写

二,字节流的FileInputStream

  此类是InputStream的子类,主要从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。用于读取诸如图像数据之类的原始字节流。

 1     private static void function_demo2() throws IOException {
 2         // 创建字节文件读取流对象,使用了里氏替换原则,也可以把前面的InputStream is换成FileInputStream is
 3         InputStream is = new FileInputStream("byte.txt");
 4         int ch;//存储读取到的字节数
 5         while ((ch = is.read()) != -1) {//循环读取
 6             System.out.println((char) ch);
 7         }
 8         is.close();//关闭流
 9 
10     }

  带缓冲数组的读取

1     private static void function_demo3() throws IOException {
2         FileInputStream fs = new FileInputStream("byte.txt");// 字节读取流对象
3         // 缓冲数组,注意此数组中的大小用的是读取流对象的availabel方法,此方法可获取需要被读取的文件的大小,但此方法在
4         // 读取大文件的时候不建议用,会把内容占满
5         byte[] by = new byte[fs.available()];
6         fs.read(by);// 因上面的字节数组的大小刚好为文件的大小,所以可调用read(byte[] b)方法把内容一次性读取到数组中去
7         System.out.println(new String(by));
8         fs.close();
9     }

 三,字节流的FileOutputStream

  此类实现了OutputStream,但是此类没有flush方法,若调用flush方法其实调的是其父类的。

1     private static void function_demo1() throws IOException {
2         OutputStream os = new FileOutputStream("byte.txt");//字节写入流对象
3         os.write(new String("abc").getBytes());
4         os.flush();
5         os.close();
6     }
四,用字节流实现文件复制
 1     private static void function_demo4() throws IOException {
 2         FileInputStream inputStream = new FileInputStream("demo.txt");//字节读取流对象
 3         FileOutputStream outputStream = new FileOutputStream("byte2.txt");//字节写入流对象
 4         byte[] bt = new byte[1024];//缓冲数组,每次读到的内容存放到此数组中
 5         int ch;//每次读到的字节数
 6         while ((ch = inputStream.read(bt)) != -1) {//如果读取了最后则read方法返回-1
 7             outputStream.write(bt, 0, ch);//把读到的内容写入到新文件中
 8         }
 9         outputStream.close();//关闭流
10         inputStream.close();//关闭流
11     }

   用原始的字节流复制一个avi文件是非常慢的,因为它是一个字节一个字节的去读的。下面的例子是复制一个15M大小的文件,可以把程序运行起来后切到文件的目录明显的看到文件大小值的跳动,代码如下:

 1   private static void function_demo5() throws IOException {
 2         FileInputStream inputStream = new FileInputStream("1.avi");// 字节读取流对象
 3         FileOutputStream outputStream = new FileOutputStream("2.avi");// 字节写入流对象
 4         int ch;
 5         while ((ch = inputStream.read()) != -1) {
 6             outputStream.write(ch);
 7             outputStream.flush();
 8         }
 9         outputStream.close();
10         inputStream.close();
11     }

   高效的读取

 1     private static void function_demo6() throws IOException {
 2         FileInputStream inputStream = new FileInputStream("1.avi");
 3         FileOutputStream outputStream = new FileOutputStream("3.avi");
 4         byte[] bt = new byte[inputStream.available()];//此方法创建数组的方式不建议用,若文件很大的话容易内存溢出
 5         int ch;
 6         while ((ch = inputStream.read(bt)) != -1) {
 7             outputStream.write(bt, 0, ch);
 8         }
 9         outputStream.close();
10         inputStream.close();
11     }

 五,读取键盘录入

  Java提供了System类,此类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性和环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。其中静态字段in返回一个输入流对象,此字段就是标准的输入流。演示如下

1     private static void function_demo7() throws IOException {
2         InputStream in = System.in;
3         System.out.println(in.read());
4         
5     }

  注:标准输入流是不可以调用close方法去关闭的,当关闭后无法再次打开,若强行再调用InputStream in =System.in;会抛出Stream closed异常

六,实现以下需求:读取键盘输入的字符,并转为大写输入到控制台,若输入了over则结束输入

 1   private static void function_demo8() throws IOException {
 2         InputStream in = System.in;
 3         StringBuilder sb = new StringBuilder();
 4         int ch;
 5         while ((ch = in.read()) != -1) {
 6             if (ch == '\r') {
 7                 continue;
 8             }
 9             if (ch == '\n') {
10                 if (sb.toString().equals("over")) {
11                     break;
12                 } else {
13                     System.out.println(sb.toString().toUpperCase());
14                 }
15                 sb.delete(0, sb.length());
16             } else {
17                 sb.append((char)ch);
18             }
19 
20         }
21     }

   可以注意到:为了完成上面的需要,我们用字节读取的方式有些麻烦,无论从实现还是从逻辑上来说,都不是太简单的事儿,我们记得流中曾经有一个方法叫readLine,但是这个方法属于BufferedReader这个类,属于字符流中的方法,那么有没有办法把字节流转为字符流从而可以调用这个方法呢?

七,转换流InputStreamReader

  InputStreamReader类是字节流通向字符流的桥梁,它属于字符流,继承自Reader;它使用指定的编码方式读取字节并将其解码为字符。它使用的编码方式可以由名称指定或显式给定,或者可以接受平台默认的字符集。

 

  每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。

 

  为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:

 

   BufferedReader in   = new BufferedReader(new InputStreamReader(System.in));
 用转换流实现上面的需求如下:
 1     private static void function_demo9() throws IOException {
 2         InputStream inputStream = System.in;
 3         InputStreamReader reader = new InputStreamReader(inputStream);
 4         BufferedReader br = new BufferedReader(reader);
 5         String line = null;
 6         try {
 7             while ((line = br.readLine()) != null) {
 8                 if (line.equals("over")) {
 9                     break;
10                 } else {
11                     System.out.println(line.toUpperCase());
12                 }
13             }
14         } catch (Exception e) {
15             e.printStackTrace();
16         }finally {
17             br.close();
18         }
19     }
用转换读取流与写入流实现以上的需求
 1 private static void function_demo11() throws IOException {
 2         InputStreamReader in = new InputStreamReader(System.in);
 3         OutputStreamWriter out = new OutputStreamWriter(System.out);
 4         BufferedReader br = new BufferedReader(in);
 5         BufferedWriter bw = new BufferedWriter(out);
 6         String line = null;
 7         while ((line = br.readLine()) != null) {
 8             if (line.equals("over")) {
 9                 break;
10             } else {
11                 bw.write(line.toUpperCase());
12                 bw.flush();
13             }
14         }
15     }
用转换流实现文件读写
 1   private static void function_demo10() throws IOException {
 2         InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("demo.txt"));
 3         BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
 4         OutputStreamWriter writer = new OutputStreamWriter(System.out);
 5         BufferedWriter bw = new BufferedWriter(writer);
 6         String line = null;
 7         try {
 8             while ((line = bufferedReader.readLine()) != null) {
 9                 bw.write(line);
10                 bw.flush();
11                 bw.newLine();
12             }
13         } catch (Exception e) {
14             e.printStackTrace();
15         } finally {
16             bw.close();
17             bufferedReader.close();
18         }
19 
20     }

 八,IO流总结

  关于IO流,大致说了这么几个:

  字符流:

      读  Reader  FileReader

      写  Writer  FileWriter

    高效的字符流:

      读  BufferedReader

      写  BufferedWriter

    转换流:

      读  InputStreamReader

      写  OutputStreamWriter

  写节流:

      读  InputStream  FileInputStream

      写  OutputStream  FileOutputStream

    高效的字节流:

      读  BufferedInputStream

      写  BufferedOutputStream

  IO流有很多,体系很庞大,在用的时候难免会感觉无从下手,讲起来具体的某一个时说的天花乱坠,但具体个人去操作时不知所措。因此我们可以总结一下IO流的操作规律,明确几个操作点再用IO流的时候便会得心应手。

  1,明确源和目的

    源:InputStream  Reader

    目的:OutputStream  Writer

  2,明确是否纯文本数据

    源:是纯文本  Reader

      否  InputStream

    目的:是纯文本  Writer

      否  OutputStream

  至此可明确具体用IO流中的哪个体系处理问题

  3,明确具体设备

    源设备:

      硬盘:File

      键盘:System

      内存:数组

      网络:Socket流

    目的设备:

      硬盘:File

      键盘:System

      内存:数组

      网络:Socket流

  4,是否需要其它功能

    需要高效:

        是:加上buffered

  以下四个需求对应于四个明确

  1,复制文本

    a,明确源和目的

      源:InputStream  Reader

      目的:OutputStream  Writer

    b,是否是纯文本

      是:

        源:Reader

        目的:Writer

    c,明确具体设备

      源:

        硬盘  File

      目的:

        硬盘  File

    从而确定需要使用的IO体系为下:

    FileReader fr = new FileReader("a.txt");

    FileWriter fw = new FileWriter("b.txt");

   d,需要额外功能吗?

    是,需要高效

    BufferedReader br = new BufferedReader(fr); 

    BufferedWriter bw = new BufferedWriter(fw);

  2,读取键盘录入信息,并写入到文本中

    a,明确源和目的

      源:InputStream  Reader

      目的:OutputStream  Writer

    b,是否纯文本

      是:

        源:Reader

        目的:Writer

    c,明确具体设备

      源:

        键盘  System

      目的:

        硬盘  File

    InputStream in = System.in;

    FileWriter fw=new FileWriter("b.txt");

    d,需要额外功能吗?

      读取的时候用的是字节流,写出的时候用的是字符流,所以操作起来不太方便,需要转换

      InputStreamReader inReader=new InputStreamReader(System.in);

      想高效

      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

      BufferedWriter writer = new BufferedWriter(new FileWriter("b.txt"));

  3,将一个文本数据显示在控制台上

    a,明确源和目的

      源:Reader  InputStream

      目的:Writer  OutputStream

    b,是否是纯文本

      是

      源:Reader

      目的:Writer

    c,明确具体设备

      源:硬盘  FileReader

      目的:控制台即内在  System

      FileReader reader=new FileReader("a.txt");

      OutputStream out=System.out;

    d:额外功能

      转换并高效

      BufferedReader br=new BufferedReader(reader);

      BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));

  4,读取键盘录入的数据,显示在控制台上

    a,明确源和目的

      源:Reader  InputStream

      目的:Writer  OutputStream

    b,是否是纯文本

      是

      Reader

      Writer

    c,明确具体设备

      源:键盘  

      InputStream input=System.in;

      目的:控制台  

      OutputStream out=System.out;

     d,额外功能

        因为操作的都是字节流,但是却要用字符的形式输出,所以需要转换

        InputStreamReader inputStream=new InputStreamReader(System.in);

        OutputStreamWriter outputStream=new OutputStreamWriter(System.out);

        需要高效

        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));