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 }