IO流之字节流字符流理解

目录:

  一、什么是IO流 ;

  二、流的分类;

  三、字节流解析;

  四、字符流解析;

  五、序列流(合并流);


 

一、什么是IO流 

  简单说就是将数据(文本,音频,视频,图片等)以流的形式进行读写。 举个栗子,比如自来水厂跟你家,中间传输的是水流,用完之后还必须关闭水龙头。

二、流的分类

  流按照操作数据的类型分为:字节流,字符流;

  流按照流向分为:输入流,输出流;

三、字节流解析

  万能的字节可以表示任何数据,比如文本,音频,视频,图片等都是作为字节存在的;

  字节流的抽象基类:

    输入流:InputStream,以程序为参照物输入到程序;

    输出流:OutputStream,以程序为参照物输出程序;

  注意点:因为是抽象类所以无法被实例化,实际应用中都是用的实现类:FileInputStream,ByteArrayInputStream等;

 3.1 InputStream 输入流 

  3.1.1、read()读取输入流

  read()方法是一个字节一个字节读取;

 1 package com.zyy.stop;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.IOException;
 5 
 6 public class Zyyprotest {
 7     
 8     public static void main(String[] args) throws IOException {
 9         //打开流
10         FileInputStream fis = new FileInputStream("C:\\test/test.txt");
11         
12         int len = 0;
13         
14         while ((len = fis.read()) != -1) {
15             System.out.println((char)len);
16         }
17         
18         // 一定要关闭流,否则会浪费资源
19         fis.close();
20     }
21 }

  3.1.2 read(byte[] byt)读取输入流

  使用缓冲区(byte[])进行读写,这样将读取到的数据存储到字节数组中,再一次性操作字节数组,可以提高效率;

 1 package com.zyy.stop;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.IOException;
 5 
 6 public class Zyyprotest {
 7     
 8     public static void main(String[] args) throws IOException {
 9         //打开流
10         FileInputStream fis = new FileInputStream("C:\\test/test.txt");
11         
12         //定义缓冲区
13         byte[] byt = new byte[1024];
14         
15         int len = fis.read(byt);
16         
17         for (int i = 0; i < len; i++) {
18             System.out.println((char)byt[i]);
19         }
20         
21         // 一定要关闭流,否则会浪费资源
22         fis.close();
23     }
24 }

  3.1.3 read(byte[] byt, int off, int len)读取输入流

  byt表示缓冲区,off表示从字节数组的什么位置开始存数据,len表示字节数组存储多少个数据;

 1 package com.zyy.stop;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.IOException;
 5 
 6 public class Zyyprotest {
 7     
 8     public static void main(String[] args) throws IOException {
 9         //打开流
10         FileInputStream fis = new FileInputStream("C:\\test/test.txt");
11         
12         //定义缓冲区
13         byte[] byt = new byte[1024];
14         
15         int len = fis.read(byt, 0, byt.length);
16         
17         for (int i = 0; i < len; i++) {
18             System.out.println((char)byt[i]);
19         }
20         
21         // 一定要关闭流,否则会浪费资源
22         fis.close();
23     }
24 }

  3.1.4 输入流Skip()方法

  需求:读取test.txt文件中第5个字节开始后的所有数据,也就是跳过前4个字节;

  方法:我们只需要在读取之前设置一下fis.read (number) 即可;

1      //打开流
2         FileInputStream fis = new FileInputStream("C:\\test/test.txt");
3         fis.skip(4); //设置skip值

  3.1.5 缓冲区 + 循环读取文件的所有内容

  前面介绍了逐个字节读取文件与利用缓冲区读取,其中加入缓冲区提高了效率,但是也只是读取了文件中的一段(与设置的缓冲区大小一样),这里介绍读取完整的文件;

 1 package com.zyy.stop;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.IOException;
 5 
 6 public class Zyyprotest {
 7     
 8     public static void main(String[] args) throws IOException {
 9         FileInputStream fis = new FileInputStream("C:\\test/test.txt");
10         
11         byte[] byt = new byte[1024];
12         
13         int len = 0;
14         while ((len = fis.read(byt)) != -1) {
15             //将字节转字符串,并输出
16             System.out.println(new String(byt, 0, len));
17         }
18         
19         fis.close();
20     }
21 }  

 3.2 OutPutStream输出流                          

  3.2.1 write(int b) 写出输出流

  write(int b) 是一个字节一个字节的输出;

 1 package com.zyy.stop;
 2 
 3 import java.io.FileOutputStream;
 4 import java.io.IOException;
 5 
 6 public class Zyyprotest {
 7     
 8     public static void main(String[] args) throws IOException {
 9         //指定文件如不存在,则会自己创建
10         String path = "C:\\test/zyy.txt";
11         FileOutputStream fos = new FileOutputStream(path);
12         fos.write('B');
13         fos.write('O');
14         fos.write('Y');
15         fos.close();
16     }
17 }

  3.2.2 write(byte[] byt)写出输出流

  与输入流中提到的缓冲区一样,输出流中也可以加入缓冲区来提高写出的效率;

 1 package com.zyy.stop;
 2 
 3 import java.io.FileOutputStream;
 4 import java.io.IOException;
 5 
 6 public class Zyyprotest {
 7     
 8     public static void main(String[] args) throws IOException {
 9         //方法1:新内容会将文件中的内容覆盖
10         FileOutputStream fos1 = new FileOutputStream("C:\\test/zyy.txt");
11         fos1.write("Girl".getBytes());
12         fos1.close();
13         
14         //方法2:新内容会接在文件中的内容后面
15         FileOutputStream fos2 = new FileOutputStream("C:\\test/zyy.txt", true);
16         fos2.write("AndBoy".getBytes());
17         fos2.close();
18     }
19 }

 3.3 字节输入输出流综合使用  

  学习了字节的输入输出流,必须结合起来使用,否则就达不到学习的目标,身为一个程序员就是要多写代码;

 1 package com.zyy.stop;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 
 7 public class Zyyprotest {
 8     
 9     public static void main(String[] args) throws IOException {
10         writeContext("C:\\test/zyy.txt", "IloveCaiyan");
11         readContext("C:\\test/zyy.txt");
12     }
13     
14     public static void writeContext(String path, String context) throws IOException {
15         FileOutputStream fos = new FileOutputStream(path);
16         fos.write(context.getBytes());
17         fos.close();
18     }
19     
20     public static void readContext(String path) throws IOException {
21         FileInputStream fis = new FileInputStream(path);
22         
23         int len = 0;
24         byte[] byt = new byte[1024];
25         while ((len = fis.read(byt)) != -1) {
26             System.err.println(new String(byt, 0, len));
27         }
28         fis.close();
29     }
30 }

  3.4文件拷贝

  先来看下面的代码,这里以拷贝图片为例:

 1 package com.zyy.stop;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 
 7 public class Zyyprotest {
 8     
 9     public static void main(String[] args) throws IOException {
10         copyImg("C:\\test/timg.jpg", "C:\\test/target.jpg");
11     }
12     
13     public static void copyImg (String getPath, String storePath) throws IOException {
14         FileInputStream fis = new FileInputStream(getPath);
15         FileOutputStream fos = new FileOutputStream(storePath);
16         
17         int len = 0;
18         while ((len = fis.read()) != -1) {
19             fos.write(len);
20         }
21         
22         fis.close();
23         fos.close();
24     }
25 }

  这段代码除了没有处理异常,单纯的完成复制是没有问题的,但是效率太低了,我们学了缓冲区啊缓冲区,换上,改良后的代码如下:

 1 package com.zyy.stop;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 
 7 public class Zyyprotest {
 8     
 9     public static void main(String[] args) throws IOException {
10         copyImg("C:\\test/timg.jpg", "C:\\test/target.jpg");
11     }
12     
13     public static void copyImg (String getPath, String storePath) throws IOException {
14         FileInputStream fis = new FileInputStream(getPath);
15         FileOutputStream fos = new FileOutputStream(storePath);
16         
17         int len = 0;
18         byte[] byt = new byte[1024]; //加入缓冲区提高拷贝效率
19         while ((len = fis.read(byt)) != -1) {
20             fos.write(byt);
21         }
22         
23         fis.close();
24         fos.close();
25     }
26 }

  这样就可以了么?当然不是,大家可以分别右击timg.jpg与target.jpg的属性,你会发现他们的大小是不一样的,拷贝后的文件多了一些东西。

  这是因为最后一次读取容器(byte[])未装满,也就是你定义的byte[1024]但最后一次读取只读到了24个字节,但是写出的时候还是写出了1024,多出了1000个字节。

  那么我们的措施就是让它读多少就写多少,再次改良后的代码如下。

 1 package com.zyy.stop;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 
 7 public class Zyyprotest {
 8     
 9     public static void main(String[] args) throws IOException {
10         copyImg("C:\\test/timg.jpg", "C:\\test/target.jpg");
11     }
12     
13     public static void copyImg (String getPath, String storePath) throws IOException {
14         FileInputStream fis = new FileInputStream(getPath);
15         FileOutputStream fos = new FileOutputStream(storePath);
16         
17         int len = 0;
18         byte[] byt = new byte[1024];
19         while ((len = fis.read(byt)) != -1) {
20             fos.write(byt, 0, len); //读多少字节就写出多少字节
21         }
22         
23         fis.close();
24         fos.close();
25     }
26 }

  很明显现在就差最后一步了,处理异常,为什么要处理异常,不能仅仅是抛出异常呢?

  这是因为一旦有异常,程序是执行不到流的close()方法的,这样你操作的文件就会一直被占用着无法被操作,这就很蛋疼了。

  所以我们的目标是:必须保证close()方法一定要被执行到!so,最终代码如下:

 1 package com.zyy.stop;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 
 7 public class Zyyprotest {
 8     
 9     public static void main(String[] args) throws IOException {
10         copyImg("C:\\test/timg.jpg", "C:\\test/target.jpg");
11     }
12     
13     public static void copyImg (String getPath, String storePath) {
14         FileInputStream fis = null;
15         FileOutputStream fos = null;
16         try {
17             fis = new FileInputStream(getPath);
18             fos = new FileOutputStream(storePath);
19             
20             int len = 0;
21             byte[] byt = new byte[1024];
22             
23             while ((len = fis.read(byt)) != -1) {
24                 fos.write(byt, 0, len);
25             }
26             
27         } catch (IOException e) {
28             throw new RuntimeException(e);
29         } finally {
30             if (fis != null) {
31                 try {
32                     fis.close();
33                 } catch (IOException e) {
34                     throw new RuntimeException(e);
35                 } finally {
36                     if (fos != null) {
37                         try {
38                             fos.close();
39                         } catch (IOException e) {
40                             throw new RuntimeException(e);
41                         }
42                     }
43                 }
44             }
45         }
46         
47     }
48 }

     3.5 字节缓冲流

  [1] 之前提到过为了提高流的读写效率我们采用了自己设置缓冲区,其实Java提供了专门的字节流缓冲来提高效率:BufferedInputStream与BufferedOutputStream;

  [2] BufferedInputStream与BufferedOutputStream可以通过减少读写次数,来提高流的读写速度,如果没有指定缓冲区大小则默认为1024 * 8;

  [3] 缓冲区输入流与缓冲区输出流必须要配合使用,缓冲区输入流会将数据读取到缓冲区,当缓冲区满时或者调用flush() 方法时,缓冲区输出流会将数据输出;

 1 package com.zyy.stop;
 2 
 3 import java.io.BufferedInputStream;
 4 import java.io.BufferedOutputStream;
 5 import java.io.FileInputStream;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 
 9 public class Zyyprotest {
10     
11     public static void main(String[] args) throws IOException {
12         copyImg("C:\\test/timg.jpg", "C:\\test/target.jpg");
13     }
14     
15     public static void copyImg (String getPath, String storePath) throws IOException {
16         FileInputStream fis = new FileInputStream(getPath);
17         FileOutputStream fos = new FileOutputStream(storePath);
18         
19         BufferedInputStream bis = new BufferedInputStream(fis);
20         BufferedOutputStream  bos = new BufferedOutputStream(fos);
21         
22         int len = 0;
23         while ((len = bis.read()) != -1) {
24             bos.write(len);
25         }
26         
27         bis.close();
28         bos.close();
29     }
30 }

四、字符流解析 

  之前我们介绍的字节流是以字节为单位进行操作的,但是字节处理字符信息并不是很方便(会出现显示乱码),那么字符流(字节流 + 编码表)就诞生了,可以专门处理字符。

  字符流的抽象基类:

    输入流:reader,以程序为参照物输入到程序;

    输出流:writer,以程序为参照物输出程序;

  4.1 Reader输入流

 1 package com.zyy.stop;
 2 
 3 import java.io.FileReader;
 4 import java.io.IOException;
 5 import java.io.Reader;
 6 
 7 public class Zyyprotest {
 8     
 9     public static void main(String[] args) throws IOException {
10         readFile("C:\\test/zyy.txt");
11     }
12     
13     public static void readFile (String getPath) throws IOException {
14         Reader fr = new FileReader(getPath);
15         
16         int len = 0;
17         while ((len = fr.read()) != -1) {
18             System.out.println((char)len);
19         }
20         
21         fr.close();
22     }
23 }

  4.2 Writer输出流 

 1 package com.zyy.stop;
 2 
 3 import java.io.FileWriter;
 4 import java.io.IOException;
 5 
 6 public class Zyyprotest {
 7     
 8     public static void main(String[] args) throws IOException {
 9         writeFile("C:\\test/zyy.txt");
10     }
11     
12     public static void writeFile (String storePath) throws IOException {
13         FileWriter fw = new FileWriter(storePath);
14         
15         fw.write("我");
16         fw.write("爱");
17         fw.write("中");
18         fw.write("国");
19         
20         fw.close();
21     }
22 }

  4.3 字符流拷贝

  一个文本文件中有中文有英文字母,有数字。想要把这个文件拷贝到别的目录中,我们可以使用字符流进行拷贝(字符流只能拷贝以字符为单位的文本文件);

  这里会遇到一个乱码的问题,新建的txt文档默认格式为ANSI,这时候读取以及写出就会出现乱码,这时候就需要用到InputStreamReader与OutputStreamWriter,在字符流缓冲区中会标注该应用;

 1 package com.zyy.stop;
 2 
 3 import java.io.FileReader;
 4 import java.io.FileWriter;
 5 import java.io.IOException;
 6 
 7 public class Zyyprotest {
 8     
 9     public static void main(String[] args) throws IOException {
10         copyFile("C:\\test/zyy.txt", "C:\\test/test.txt");
11     }
12     
13     public static void copyFile (String getPath, String storePath) throws IOException {
14         FileReader fr = new FileReader(getPath);
15         FileWriter fw = new FileWriter(storePath);
16         
17         int ch = 0;
18         char[] arr = new char[1024];
19         while ((ch = fr.read(arr)) != -1) {
20             fw.write(arr, 0, ch);
21         }
22         
23         fr.close();
24         fw.close();
25     }
26 }

  4.4 字符流异常的抛出

 1 package com.zyy.stop;
 2 
 3 import java.io.FileReader;
 4 import java.io.FileWriter;
 5 import java.io.IOException;
 6 
 7 public class Zyyprotest {
 8     
 9     public static void main(String[] args) {
10         copyFile("C:\\test/zyy.txt", "C:\\test/test.txt");
11     }
12     
13     public static void copyFile (String getPath, String storePath) {
14         FileReader fr = null;
15         FileWriter fw = null;
16         try {
17             fr = new FileReader(getPath);
18             fw = new FileWriter(storePath);
19             
20             int ch = 0;
21             char[] arr = new char[1024];
22             while ((ch = fr.read(arr)) != -1) {
23                 fw.write(arr, 0, ch);
24             }
25         } catch (IOException e) {
26             throw new RuntimeException(e);
27         } finally {
28             try {
29                 fr.close();
30             } catch (IOException e) {
31                 throw new RuntimeException(e);
32             } finally {
33                 try {
34                     fw.close();
35                 } catch (IOException e) {
36                     throw new RuntimeException();
37                 }
38             }
39         }
40     }
41 }

   4.5字符流的缓冲区

 1 package com.zyy.stop;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.BufferedWriter;
 5 import java.io.FileInputStream;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.io.InputStreamReader;
 9 import java.io.OutputStreamWriter;
10 
11 public class Zyyprotest {
12     
13     public static void main(String[] args) throws IOException {
14         copyTxt("C:\\test/a.txt", "C:\\test/b.txt");
15     }
16     
17     public static void copyTxt(String getFile, String storFile) throws IOException {
18         //FileReader fr = new FileReader(getFile);
19         //FileWriter fw = new FileWriter(storFile);
20         
21         //解决读写乱码问题
22         InputStreamReader isr = new InputStreamReader(new FileInputStream(getFile), "GBK");
23         OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(storFile), "GBK");
24         
25         BufferedReader br = new BufferedReader(isr);
26         BufferedWriter bw = new BufferedWriter(osw);
27         
28         String line = null;
29         while ((line = br.readLine()) != null) {
30             bw.write(line);
31             bw.flush();
32             bw.newLine();
33         }
34         
35         br.close();
36         bw.close();
37     }
38 }

 

 五、序列流(合并流)

  SequenceInputStream :表示其他流的逻辑串联,它从输入流的有序集开始,并从第一个输入流开始读取,直到到达文件的末尾,接着从第二个流开始读取,依次类推直到把所有的输入流都读取完;

  5.1文件切割拷贝

  这里以mp3文件为例

 1 package com.zyy.stop;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileOutputStream;
 6 import java.io.IOException;
 7 import java.io.SequenceInputStream;
 8 import java.util.Enumeration;
 9 import java.util.Iterator;
10 import java.util.LinkedHashSet;
11 
12 public class Zyyprotest {
13     
14     public static void main(String[] args) throws IOException {
15         //文件切割
16         splitFile(new File("C:\\test/あらきまりな.mp3"), 4, new File("C:\\test"));
17         
18         //文件合并
19         LinkedHashSet<FileInputStream> set = new LinkedHashSet<FileInputStream>();
20         set.add(new FileInputStream(new File("C:\\test/part1.mp3")));
21         set.add(new FileInputStream(new File("C:\\test/part2.mp3")));
22         set.add(new FileInputStream(new File("C:\\test/part3.mp3")));
23         set.add(new FileInputStream(new File("C:\\test/part4.mp3")));
24         mergeFile(set, new File("C:\\test/part.mp3"));
25     }
26     
27     public static void splitFile(File src, int count, File dir) throws IOException {
28         FileInputStream fis = new FileInputStream(src);
29         FileOutputStream fos = null;
30         
31         byte[] byt = new byte[1024 * 1024];
32         int len = 0;
33         for (int i = 0; i < count; i++) {
34             len = fis.read(byt);
35             if (len != -1) {
36                 fos = new FileOutputStream(dir + "/part" + (i + 1) + ".mp3");
37                 fos.write(byt, 0, len);
38             }
39             fos.close();
40         }
41         
42         fis.close();
43     }
44     
45     public static void mergeFile(LinkedHashSet<FileInputStream> set, File desert) throws IOException {
46         final Iterator<FileInputStream> it = set.iterator();
47         FileOutputStream fos = new FileOutputStream(desert);
48         
49         SequenceInputStream sis = new SequenceInputStream(new Enumeration<FileInputStream>() {
50             @Override
51             public boolean hasMoreElements() {
52                 return it.hasNext();
53             }
54 
55             @Override
56             public FileInputStream nextElement() {
57                 return it.next();
58             }
59         });
60         
61         byte[] byt = new byte[25508];
62         int len = 0;
63         while ((len = sis.read(byt)) != -1) {
64             fos.write(byt, 0, len);
65         }
66         
67         sis.close();
68         fos.close();
69     }
70 }
posted @ 2017-11-14 15:54  AudreyHepburn  阅读(231)  评论(0编辑  收藏  举报