一、概念及分类
InputStream(输入流)/OutputStream(输出流)是所有字节输入输出流的父类
【注】输入流和输出流的是按程序运行所在的内存的角度划分的
字节流操作的数据单元是8的字节,字符流操作的数据单元是16位的字符
【流的概念】
——————————
ooooooooooooooooo
—————————— (输入流模型,文件从头(左边)到尾(右边),)
↑,(记录指针)
每个‘’o“”看出一个"水滴",不管是字节流还是字符流,每个水滴是最小的输入/输出单位,对于字节流而言,每个水滴是1个字节(8位)
对于字符流而言,每个水滴是1个字符(16位)。输入流使用隐含的记录指针来表示当前正准备从哪个水滴开始读取,每当程序从InputStream或
Reader里取出一个或多个水滴后,指针自动向后(指针从左往右)移动。此外,还可以控制记录指针的移动,比如说设置偏移量off。
对于输出流相对于把水滴放入输入流水管中,也有记录指针自动控制移动,每当程序向OutputStream或Writer输出一个或多个水滴后,记录指针自
动向后移动。
1、InputStream(重要的是 文件操作字节流FileInputStream子类和带8KB缓冲数组的ButteredInputStream)
此抽象类是表示字节输入流的所有类的父类。InputSteam是一个抽象类,它不可以实例化。 数据的读取需要由它的子类来实现。根据节点的不同,
它派生了不同的节点流子类 。 继承自InputSteam的流都是用于向程序中输入数据,且数据的单位为字节(8 bit)。
int read():从(所写程序的源文件中)一个一个字节读取数据,并将字节的值作为int类型返回(0-255之间的一个值)。
如果未读出字节则返回-1(返回值为-1表示读取结束)。
int (byte b[]) :一段一段的读取数据
void close(): (通知操作系统)关闭输入流对象,释放相关系统资源。
【代码示例】字节文件操作流_不带缓冲数组 (FileInputStream)
1 /*输入流(从数据源输入到程序中) :数据源-->>程序,是从数据源(源文件、类比书本上知识)读取(read)数据到程序(目标程序、大脑) 2 * 输入流对应read()方法 ,从书本上流入(输入、Input)到大脑中,而大脑需要读取(read)书本上的知识 3 * 步骤:选择源数据-->>选择流(选哪个搬家公司)-->>操作(一个一个读/写or一段一段读/写,即怎么搬家)-->>释放系统资源(让搬家公司走) 4 * 5 一、 read():从输入流中读取一个8位的字节的数据,(程序自动)把它转成0-255之间的整数,并返回这个整数。 采用逐个读取字节 6 1.从读取流读取的是一个一个字节 7 2.返回的是字节的(0-255)内的字节值 ASCII码 8 3.读一个下次就自动到下一个,如果碰到-1说明没有值了. 9 10 二、 read(byte[] bytes): 从输入流中最多读取bytes.length(定量)个字节的数据(最后一次可能不满,前面肯定是满的),并将 11 它们存在byte数组中,返回实际读取的字节数 12 1.从读取流读取一定数量的字节,如果比如文件总共是102个字节 13 2.我们定义的数组长度是10,那么默认前面10次都是读取10个长度 14 3.最后一次不够十个,那么读取的是2个 15 4.这十一次,每次都是放入10个长度的数组. 16 17 三、read(byte[] bytes,int off ,int len):从输入流中最多读取len个字节(字节可以设定,但不超过数组最大容量)将其存在bytes 18 数组中,但放入数组的时候并不是从起点开始的是从可以自己设定的off位置开始的(当然可以设为0,表示从起点开始)返回实际读取的字节数 19 1.从读取流读取一定数量的字节,如果比如文件总共是102个字节 20 2.我们定义的数组长度是10,但是这里我们写read(bytes,0,9)那么每次往里面添加的(将只会是9个长度),就要读12次,最后一次放入3个. 21 3.所以一般读取流都不用这个而是用上一个方法:read(byte[]); 22 23 void close()关闭输入流并释放与该流相关的所有系统资源 24 */ 25 package cn.sxt.test; 26 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 32 33 public class Test_0329_InputStream { 34 public static void main(String[] args) throws IOException { //与外界(操作系统读写)有联系,可能有异常,所以抛出异常 35 //1、选择源文件 内容为:hello China I love you 36 File file=new File("编码_GBK_纯英文.txt"); //GBK编码 英文占一个字节 37 //2、选择流 FileInputStream 顺序的读取文件,只要不关闭每次调用read()方法就顺序的读取源数据中【剩余】的内容 38 InputStream iStream=new FileInputStream(file); 39 40 //这种写法也对,综合了第一步和第二步,更加简单 41 FileInputStream iStream2=new FileInputStream("编码_GBK_纯英文.txt"); 42 43 //3-1-1、有点绕的写法 测试read()方法选择操作 (读/写) 循环读取 一个一个字节去读 44 /*int temp; 45 while ((temp=iStream.read())!=-1) { 46 System.out.print((char)temp); 47 48 }*/ 49 //3-1-2、最可以让人理解的一种写法 50 /*int b=0;//b的作用记住每次读取的一个字节的整形返回值(英文、数字及常见符号其实就是ASCII码的值) 51 while (true) { 52 b=iStream.read(); 53 if (b==-1) {//如果读出字节为-1 跳出整个循环表示已经到文件末尾 54 break; 55 } 56 System.out.print((char)b); 57 }*/ 58 59 60 /*//3-2 测试read(byte[] b)方法 将数据读取到准备好的字节数组(byte[],这里名字叫datas)中,同时返回读取字节数组的实际长度 61 // 如果到结尾,返回-1 62 byte datas[]=new byte[4]; //新建一个大小为4的字节(byte)数组,名字叫datas. 63 int length=iStream.read(datas);//length=4 就是每次读取时字符的实际长度,因为读到最后是可能剩余的字符不足4 可能为3 64 //当最后读取完后,再去读取时发现没有了,则返回-1 表示读取结束 65 //String msg2=new String(datas, 0, datas.length, "GBK"); 标准解码语句 66 String msg2=new String(datas, 0, length); //字节数组解码成字符 67 System.out.println(msg2); //输出 "hell" 因为没用循环只表示一次读取的情况,一次只读取了4个字节的字符 68 //可以加循环或者加大datas数组的大小,让其一次读完 69 */ 70 71 72 73 //3-3 测试read(byte[] b,int off,int len)方法 。读取任意一段字节。off(偏移量):表示把源文件中从头(即下标)开始读取到的字节数存在 74 //datas数组下标为off开始的位置,读取源文件的长度为len,也就是在datas数组中下标为off开始位置存储长度为len字节的源数据 75 byte datas[]=new byte[7]; 76 77 //虽然定义的数组datas大小为7个字节,但每次只添加4个字节的字符,表示使用datas数组的实际使用空间为4个字节。 78 //2的含义:表示把读取到数据存在datas数组下标为2的位置 即datas[2]='h'意味着datas[0]=datas[1]=0,即null空字符 79 //4的含义:在datas数组中从下标为2的位置开始存储一段长度为4的源数据字节(从源文件开头读的) 即 80 //datas[2]=104,'h';datas[3]=101,'e';datas[4]=108,'l';datas[5]=108,'l' datas[6]=0 ,null 81 int length=iStream.read(datas, 2, 4);//read()方法返回length=4 4:代表实际存储一段长度为4的字节,其它下标位置存储为空 82 System.out.println(length); 83 for (int i=0;i<datas.length ;i++) { 84 System.out.printf("datas[%d]=%d;",i,datas[i]); 85 86 } 87 88 89 //ASCII码 (范围 0-127)null 0;a 97;b 98;c 99;d 100;e 101;f 102; g 103 ;h 104;i 105; g 106;k 107;l 108 90 //字节数组解码成字符 从数组datas下标为1的地方开始解码(只测试纯英文的情况,含中文数字可能报异常)。 91 //解码长度length=4,代表解码一段长度为4的字节码文件 输出" hel";也可以length-1 解码一段长度为3的datas数组文件 92 //输出结果为" he" 93 String msg2=new String(datas, 1, length); //String 构造方法,字节-->>字符 94 System.out.println("\n"+msg2); 95 96 97 /* 98 int data1=iStream.read();//读取第一个字符 "h" 输出的是"h"的ASCLL码104 99 int data2=iStream.read();//读取第二个字符 "e" 100 int data3=iStream.read();//读取第三个字符 "l" 101 System.out.println(data1);//返回的数据的ASCII码需要强制转型 。文件的末尾如果没有数据返回-1 102 System.out.println((char)data2); 103 System.out.println((char)data3);*/ 104 105 //4、释放资源 106 iStream.close(); 107 108 } 109 110 }
2、 OutputStream (重要的是 文件操作字节流FileOutputStream子类和带8KB缓冲数组的ButteredOutputStream子类))
此抽象类是表示字节输出流的所有类的父类。输出流接收输出字节并将这些字节发送到某个目的地。
void write(int n):向目的地(即所写程序)中写入一个字节。
void write(byte b[]):一段一段去写数据
void (byte b[],int off,int len) :从指定的字节数组写入len字节,从偏移量off开始输出到此输出流
void flush() :刷新此输出流,并强制任何缓冲的输出字节流被写出。
void close(): (通知操作系统)关闭输出流对象,释放相关系统资源。
【代码示例】
1 /* 2 *输出(Output)流(Stream)。 3 *通过写字( write()方法 ),把大脑中的知识(类比程序)输出(写出)到(Output)外界的作业本上(类比源文件,如a.txt文件),去更改作业本上原有的内容 4 步骤:创建源-->>选择流-->>操作(写出内容)-->>释放资源 5 void write(int n): 向目的地(即所写程序)中写入一个字节。逐个写入字节 6 n应是读取到的源数据编码值。如把"love"写入目标文件, 一个一个字节读,读到'l' 此时这里的n应该是'l'的ASCII码值108,依次往下读 7 while(( len=in.read() )!=-1){ 8 out.write(len); 9 } 这段程序完成文件的复制,表示读到的十进制编码值(0-255之间,英文就是ASCII码),然后把这个值送到write()方法中,让其执行写入操作 10 但是效率很低,它是读一个字节取到编码值后,然后再根据编码值去写入,循环往复。一个字节一个字节读和写.好比很多货物但每次只送一个, 11 循环往复,尽管车厢很大,只装一个,浪费油。电脑中就是浪费内存,不停进行磁盘读写,速度很慢。 12 13 void write(byte b[]):一段一段去写数据。把参数b指定的字节数组的【所有字节】写入到目标文件中去。批量写入 14 为了提高送货速度则可以每次把车厢装满,减少送货次数 车厢就是byte[]数组 批量送货,减少磁盘读写 15 void (byte b[],int off,int len) :将指定的byte数组【从偏移量off开始的len个字节】写入到目标文件。 批量写入 16 17 void flush() :刷新此输出流,并强制写出所有缓冲中的字节 18 void close() : 关闭输出流并释放系统资源 19 20 * 21 */ 22 package cn.sxt.test; 23 24 import java.io.File; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.OutputStream; 28 29 public class Test_0329_OutputStream { 30 public static void main(String[] args) throws IOException { 31 //1、给出输出流目的地(给出作业本)(与read()不同那个源文件必须存在,才可以被正确读出,这里可不存在,帮你创建源文件) 32 File file=new File("编码_GBK_输出流.txt"); 33 //2、创建指向目的地的输出流。参数:true:(前提是文件已存在)表示同意在文件末尾追加新字节数组, false:表示不同意,即覆盖目的地文件的内容 34 OutputStream oStream=new FileOutputStream(file,true); 35 //3、让输出流把字节数组写入目的地 36 String msg="I love you"; 37 byte datas[]=msg.getBytes(); 38 //ASCII码 :范围是十进制的 0-127 39 //byte:整形变量(8位,范围十进制的-128-127) 字符串-->>字节存在名叫datas的数组 :编码 40 //再次理解getBytes()方法: 此方法所做的工作就是英文字符(按GBK的方式,本电脑eclipse默认是GBK)进行编码,然后把它们的 41 //十进制编码存在datas数组中,GBK中文占3个字节,英文占1个字节.GBK兼容ACSII码,所以英文字符的编码就是它们的ACSII码 42 //而且一个datas数组下标对应一个字节,对于英文1个下标存储1个字符如:datas[0]=73对应英文字符'I' datas[1]=32 对应' '(空格) 43 //假设有中文字符则是datas[n-1][n][n+1]这3个连续的空间共同存储一个中文字符(占3个字节)。这3个空间返回的分别是这个 44 //中文字符的编码的(23-16位,15-8位,7-0位)的十进制数。 45 46 for (int i=0;i<datas.length ;i++) { 47 System.out.printf("datas[%d]=%d;",i,datas[i]); 48 //3-1:利用循环向txt文件中一个一个字节写入 49 //oStream.write(datas[i]); 50 51 } 52 //3-2 一段一段字节去写入 53 //oStream.write(datas);//把datas数组中的全部字符写入目的文件 54 55 //3-3 写入部分字节 往目的地的txt文件中写了datas数组的部分字节(从下标2(字符'l')开始,长度为8的一段字节) 56 oStream.write(datas, 2, datas.length-2); 57 58 //4、关闭输出流 59 oStream.close();; 60 } 61 62 }
【示例】利用输入流和输出流进行文件复制_不带缓冲数组 (FileInputStream)
1 /* 2 *利用输入流和输出流完成文件的复制 3 * 输入流(InputStream) 书本的知识输入到大脑中,大脑要读取(read)知识 4 * 输出流(OutputStream) 大脑中的知识要写出(write)到作业本上 5 * 1、为了减少对硬盘的读写次数,提高效率,通常设置缓存数组。相应地,读取时使用的方法为:read(byte[] b) 6 * 写入时的方法为:write(byte[ ] b, int off, int length)。 7 */ 8 package cn.sxt.test; 9 10 import java.io.File; 11 import java.io.FileInputStream; 12 import java.io.FileOutputStream; 13 import java.io.IOException; 14 import java.io.InputStream; 15 import java.io.OutputStream; 16 17 public class Test_0330_CopyFile { 18 public static void main(String[] args) throws Exception { 19 20 //copy("SongYi.jpg", "Song2.jpg"); 图片也可复制,但是要注意设置缓冲数组的大小 21 copy("src.txt", "src_copy3.txt"); 22 } 23 24 25 static void copy(String src,String dest) throws IOException{ 26 //1、获取源数据 27 File file =new File(src);//源文件 从这里读取字节 28 File file2=new File(dest);//目标文件 往这里写入字节 29 30 //2、选择流 31 InputStream iStream=new FileInputStream(file);//输入流 。从源文件(书本)输入到程序中(大脑中) 32 OutputStream oStream=new FileOutputStream(file2);//输出流。 从程序中(大脑中)输出到目标文件(作业本)上.采用的是覆盖,而不是追加 33 34 //3、选择操作 35 /*把读到数据存在butter数组从下标0开始的位置,每次存储长度为数组的长度(也即一次就读完)。若butter.length 改为4表示 36 *每次只读取源文件的(从头开始的)前4个字节,存在butter[0]--butter[3]中,至于源文件剩下的需要再次运行程序读取或加个循环 37 *对于较大的文件(如图片)可以加大缓冲数组的容量 38 */ 39 byte butter[]=new byte[1024];//缓冲数组,数组的大小为1024 。中转站 大脑 40 41 /* //3-1:整体读取写入,每次都固定读取1024个字节 42 iStream.read(butter); 43 oStream.write(butter);*/ 44 45 // 3-2 :可以选择整体读取或者部分读入(即把butter.length的数值改低).这里起到效果是整体读取与3-1是一个道理 46 iStream.read(butter,0,butter.length); 47 oStream.write(butter, 0, butter.length); 48 49 50 /*//可以具体看看读取到的内容 详细步骤 51 iStream.read(butter,0,butter.length); 52 String msg=new String(butter,0,butter.length);//字节-->>字符串:解码 53 System.out.println(msg); 54 byte datas[]=msg.getBytes();//对读取到的msg字符串进行编码:字符串-->>字节 55 oStream.write(datas, 0, datas.length); */ 56 57 58 //4、关闭流 59 oStream.close(); 60 iStream.close(); 61 62 } 63 }
【代码示例】字节文件操作流_带缓冲数组 (ButteredInputStream)大小为8192B(大小就是8KB,2^13次方,1KB=1024B,)
·
1 /** 2 * 学习 BufferedInputStream/BufferedOutputStream 缓冲字节流类 3 *复制文件 测试Java自带的在缓冲数组的输入输出流 4 */ 5 6 package cn.sxt.test; 7 8 import java.io.BufferedInputStream; 9 import java.io.BufferedOutputStream; 10 import java.io.FileInputStream; 11 import java.io.FileOutputStream; 12 import java.io.IOException; 13 14 15 public class Test_0330_Buffered { 16 public static void main(String[] args) throws IOException { 17 /*File file=new File("poem.txt"); 18 InputStream iStream=new FileInputStream(file);*/ 19 //带缓冲的输入流 缓冲区为8KB 8192B (8192个字节) 20 FileInputStream file=new FileInputStream("poem.txt"); 21 BufferedInputStream iStream=new BufferedInputStream(file); 22 23 FileOutputStream file2=new FileOutputStream("poem2.txt"); 24 BufferedOutputStream oStream=new BufferedOutputStream(file2); 25 long b=System.currentTimeMillis();//获取系统当前时间 26 int len; 27 while ( (len=iStream.read()) !=-1) { 28 oStream.write(len); //当调用read()或write()方法时 首先将读写的数据存入类内部定义好8KB的数组中,然后一次性 29 //读写到文件中,类似于前边自己自定义的大小为1KB的数组,极大提高读写效率 30 } 31 long c=System.currentTimeMillis(); 32 System.out.println("共花费:"+(c-b)+"毫秒"); 33 iStream.close(); 34 oStream.close(); 35 36 37 //不带缓存缓冲的 38 FileInputStream file3=new FileInputStream("poem.txt"); 39 FileOutputStream file4=new FileOutputStream("poem3.txt"); 40 41 long d=System.currentTimeMillis();//获取系统当前时间 42 int length;//不带缓冲区的,老办法,读取一个字节,往文件中写一个字节。循环往复直到结束。速度很慢 43 while ( (length=file3.read()) !=-1) { 44 file4.write(length); 45 } 46 long e=System.currentTimeMillis(); 47 System.out.println("共花费:"+(e-b)+"毫秒"); //很明显比带缓冲区的花费时间多 48 file3.close(); 49 file4.close(); 50 51 52 } 53 54 }