目录:
一、什么是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 }