1.IO流概述
IO流是Java用于对文件内容进行操作的方式,其主要分为两种基础流:字节流、字符流;
字节流可以对任意格式的文件进行操作,但其只能以字节的为基础来对文件进行操作,主要用于文件的复制,如果用于文件内容读取可能会出现乱码的情况(例如:由于字符格式不定其中文字符可能占2个字节也3个字节,从而造成非整个文件一起读取时的乱码);
字符流只能对文本文件进行操作,但其可以对文件中的字符进行操作(可以对读取和写入选择的字符格式进行指定),多用于文件的读取和写入;
1.字节流
字节流分为两种-一个是字节输入流,一个是字节输出流,其作用顾名思义,一个是将程序中数据写入本地文件中,一个是将本地文件中的数据写入程序中;
字节输入流:
-
FileOutputStream:字节流中的基本输出流,作用为将程序中的数据写入本地文件(字节流不受文件类型限制,可以对任意类型文件进行操作)
-
格式:FileOutputStream 对象名=new FileOutputStream(参数一,参数二);
参数一可以是字符串也可以是一个File对象(当传入字符串时底层自动创建File对象),参数二为续写判断不写默认false
操作步骤:创建对象、写出数据、释放资源(如果不释放资源则文件将一直处于打开状态无法删除且占用内存,所以对于绝大多数的IO流在操作完成后都必须释放资源)注意:
-
在创建对象时如果文件存在则追加写入(如果创建对象时没有传入续写参数则默认不续写,先清空文件再追加写入,如果传入续写参数则不清空文件,则在文件内容末尾追加写入)
-
在创建对象时如果文件不存在但其父级路径存在则创建文件并打开写入
-
在创建对象时如果文件不存在并且其父级路径不存在则报错
-
在写出数据时,只能写入byte类型的数据,其实际代表的是byte数据所代表的Ascii字符,写入时有三种写入选择:
1.单个byte数据写入 2.写入一个byte数组 3.传入一个byte数组,再选择写入数据在数组中的开始位置,和写入数据个数
-
换行写入,即在需要换行的地方加上一个换行符就行,但是不同的操作系统中的换行符可能不一样,例如:
在Windows系统中换行符为:\r\n(Java对于此进行了一定优化,只需写一个\r或\n就可以实现换行,但是依旧建议不要省略),
Linux系统中则是:\n,
mac操作系统中为:\r -
续写(不覆盖),在FileOutputStream构造方法中的第二个参数为布尔类型的参数其作用为判断是否进行续写(默认为false不续写,当传入参数true使其进行续写时则,再打开文件进行写入内容就不会覆盖原来的内容了)
File fl = new File("File_Experiment\\aaa\\aaa.txt");
FileOutputStream fip = new FileOutputStream(fl);
fip.write(98);
fip.write(97);
fip.write(99);//写入99字符,实际代表ASCII码中99代表的字符
fip.close();//关闭流
FileOutputStream fip1 = new FileOutputStream(fl);
byte[] a = {99, 101, 99, 87};
fip1.write(a, 1, 2);//传入一个字节数组,从第1位开始传入2个数据
fip1.close();
FileOutputStream fip2 = new FileOutputStream(fl);
byte[] b = {99, 101, 99, 87};
fip2.write(b);
fip2.close();
// 换行写入
FileOutputStream fip3 = new FileOutputStream(fl);
String str = "仰天大笑出门去\r\n我辈岂是蓬蒿人";
byte[] bt = str.getBytes(StandardCharsets.UTF_8);//将字符串转换成byte数组,且以UTF-8的格式转换
fip3.write(bt);
fip3.close();
// 续写(不覆盖),在FileOutputStream构造方法中的第二个参数为布尔类型的参数其作用为判断是否进行续写,
// 默认为false不续写,当传入参数true使其进行续写时则,再打开文件进行写入内容就不会覆盖原来的内容了
FileOutputStream fip4 = new FileOutputStream(fl, true);
String str1 = "\r\n他人即地狱";
byte[] bt1 = str1.getBytes(StandardCharsets.UTF_8);//将字符串转换成byte数组,且以UTF-8的格式转换
fip4.write(bt1);
fip4.close();
FileOutputStream fip5 = new FileOutputStream(fl);
byte[] b1 = {99, 101, 99, 87};
fip5.write(b1);
fip5.close();
字节输入流:
FileInputStream:字节流中的基本输入流,作用为将本地文件中的数据读取进程序(字节流不受文件类型限制,可以对任意类型文件进行操作)
格式:FileOutputStream 对象名=new FileOutputStream(参数一);
-
参数一可以是字符串也可以是一个File对象(当传入字符串时底层自动创建File对象)
操作步骤:创建对象、读取数据、释放资源(如果不释放资源则文件将一直处于打开状态无法删除且占用内存,所以对于绝大多数的IO流在操作完成后都必须释放资源) -
注意:
-
对于比较小的文件的读取可以直接使用read()的空参进行一位一位的读取,而面对大文件时一次读取一位效率太慢,所以建议使用read的方法重载read(byte[] a)
此方法可以实现一次读取多位(一次读取多少位取决于传入参数数组的大小,虽然一次读取的位数越多效率越高但是创建数组也需要占用内存空间),此重载方法返回此次读取的字节个数,当整个文件读取完毕后返回-1,值得注意的是,虽然每次读取都会尽可能的将数组装满,但是由于数组中的内容是使用覆盖的方法进行写入的,如果最后一次读取的数据小于数组长度,则数组中除了新读取的数据后面也会存在旧的数据。 -
在创建对象时如果文件不存在则直接报错,由于输入流的重点为读取数据,所以不会新建文件
-
在读取数据时只能获取其的Ascii码值,返回int类型,读取过程类似迭代器,不会自动重置指针
-
当读取完所有文件数据后依旧读取则返回-1(即所有数据读取完毕后继续读取空内容或对空文件进行读取时都是返回-1)。
File str = new File("File_Experiment\\aaa\\aaa.txt");
FileInputStream fout = new FileInputStream(str);
char s = (char) fout.read();
System.out.println("获取一个的数据的结果为:" + s);
fout.close();
long i = str.length();
FileInputStream fout1 = new FileInputStream(str);
System.out.println("获取整个文件数据的结果为:");
for (long l = 0; l < i; l++) {
char s1 = (char) fout1.read();
System.out.print(s1);
}
fout1.close();
System.out.println();
FileInputStream fout2 = new FileInputStream(str);
System.out.println("获取整个文件数据的结果为:");
int j;
while ((j = fout2.read()) != -1) {
System.out.print((char) j);
}
fout2.close();
FileInputStream fout3 = new FileInputStream(str);
// 一次读取多个文件
byte[] bytes=new byte[3];
int len=fout3.read(bytes);
System.out.println(len+"是本次读取的数据个数");
System.out.println("本次读取的数据为"+Arrays.toString(bytes));
fout3.close();
FileInputStream fout4 = new FileInputStream(str);
int k;
System.out.printf("多次读取遍历结果:");
while((k=fout4.read(bytes))!=-1){
System.out.println(new String(bytes,0,k));
}
System.out.println();
fout4.close();
2.字符流
与字节流类似,字符流也是分为两种-一个是字符输入流,一个是字符输出流,其作用顾名思义,一个是将程序中数据写入本地文件中,一个是将本地文件中的数据写入程序中;
字符输入流:
FileReader:字符流中的基本输入流,作用为将本地文件中的数据读取进程序(字符流用于纯文本文件读取,可以识别各种字符编码格式,并且自动转码,在底层其内存存在一个缓冲区(8192字节),每次读取时都会尽量装满缓冲区,当缓冲区存满时或主动使用关闭流(close)时将数据传入程序
对于空参的read方法从缓冲区读取数据时,一次读取一个字节,遇到中文一次读取多个字节,然后将读取的字节解码转换成对应的十进制返回
对于有参的read方法则是将读取、解码和强转三步结合在一起,强转之后存入数组返回。
例如:对于UTF-8格式的文件在读取英文时一个字节一个字节的转换,而当读取到中文则三个字节一转换,其他字符类似都能识别其格式)
格式:FileReader 对象名=new FileReader(参数一);
参数一可以是字符串也可以是一个File对象(当传入字符串时底层自动创建File对象)
操作步骤:创建对象、读取数据、释放资源(如果不释放资源则文件将一直处于打开状态无法删除且占用内存,所以对于绝大多数的IO流在操作完成后都必须释放资源)
注意:
当使用无参的read()时返回的是读取文件字节码转换成的十进制数,而当使用有参read(char[] chars)时返回的则是本次读取的字符数,读取到的数据底层转换成char类型字符后存入传入的数组中,
当数据全部读取完毕返回-1(和FileInput的read()方法类似,其底层使用的也是字节流,只是对于编码格式做了一些操作)
File f=new File("File_Experiment\\aaa\\xas.txt");
char[] chars=new char[4];
FileReader fr_1=new FileReader(f);
int content;
System.out.println("FileReader无参read读取:");
while((content=fr_1.read())!=-1){
System.out.print((char) content);
}
System.out.println();
fr_1.close();
FileReader fr_2=new FileReader(f);
System.out.println("FileReader有参read读取:");
while((content=fr_2.read(chars))!=-1){
System.out.print(new String(chars,0,content));
}
fr_2.close();
字符输出流:
FileWriter:字符流中的基本输出流,作用为将程序中的数据写入本地文件(字符流用于纯文本文件读取,可以识别各种字符编码格式,并且自动转码,其内存存在一个缓冲区(8192字节),每次读取时都会尽量装满缓冲区,当缓冲区存满时或主动使用flush(关闭缓冲区但不关闭流)以及关闭流(close)时将数据传入本地文件
例如:对于UTF-8格式的文件在读取英文时一个字节一个字节的转换,而当读取到中文则三个字节一转换,其他字符类似都能识别其格式)
格式:FileWriter 对象名=new FileWriter(参数一,参数二);
参数一可以是字符串也可以是一个File对象(当传入字符串时底层自动创建File对象),参数二为续写判断不写默认false
操作步骤:创建对象、写出数据、释放资源(如果不释放资源则文件将一直处于打开状态无法删除且占用内存,所以对于绝大多数的IO流在操作完成后都必须释放资源)
注意:
-
在创建对象时如果文件存在则追加写入(如果创建对象时没有传入续写参数则默认不续写,先清空文件再追加写入,如果传入续写参数则不清空文件,则在文件内容末尾追加写入)
-
在创建对象时如果文件不存在但其父级路径存在则创建文件并打开写入
-
在创建对象时如果文件不存在并且其父级路径不存在则报错
-
在写出数据时,可以写入int类型也可以写入String(也可以是char类型的数组)类型的数据,int类型其实际代表的是int数据所代表的字符集(Unicode)代表的字符,写入时有三种写入选择:
1.单个int、String数据写入 2.写入一个char数组 3.传入一个char数组或String字符串,再选择写入数据在数组中的开始位置,和写入字符个数
-
换行写入,即在需要换行的地方加上一个换行符就行,但是不同的操作系统中的换行符可能不一样
例如: 在Windows系统中换行符为:\r\n(Java对于此进行了一定优化,只需写一个\r或\n就可以实现换行,但是依旧建议不要省略), Linux系统中则是:\n, mac操作系统中为:\r// 续写(不覆盖),在FileOutputStream构造方法中的第二个参数为布尔类型的参数其作用为判断是否进行续写
默认为false不续写,当传入参数true使其进行续写时则,再打开文件进行写入内容就不会覆盖原来的内容了
File f=new File("File_Experiment\\aaa\\aaa.txt");
char[] c={'奈','何','明','月','照','沟','渠','\n'};
FileWriter fw_1=new FileWriter(f);
fw_1.write("ass");
fw_1.write(97);
fw_1.write(c);
fw_1.write(c,0,2);
fw_1.flush();//在缓冲区还未装满时强制将缓冲区数据存入本地文件,但不关闭流
fw_1.write("\n我本将心比明月",0,3);
fw_1.close();
FileWriter fw_2=new FileWriter(f);
fw_2.write("不续写");
fw_2.close();
FileWriter fw_3=new FileWriter(f,true);
fw_3.write("\n续写");
fw_3.close();
3.高级流
所谓高级流就是在基础的字符流和字节流上进行了一个封装,增加了一些特定的功能,但其底层依旧使用的是字符流和字节流
1.缓冲流
所谓缓冲流就是在字符流和字节流的基础上在内存中增加了一个缓冲区(8192字节),以此提高文件读取与写入的效率
1.字节缓冲流
// 字节缓冲流:其虽然底层依旧是使用字节流来进行内存和文件的数据传输,其内存存在一个缓冲区(8192字节),
// 和字符流一样每次读取时都会尽量装满缓冲区,当缓冲区存满时或主动使用flush(关闭缓冲区)以及关闭流(close)时将数据传入本地文件
// 由此来加快读取速度,读取和写入的缓冲区不是同一个
File f= new File("File_Experiment\\aaa\\aaa.txt");
BufferedInputStream bfit=new BufferedInputStream(new FileInputStream(f));//创建一个字节输入缓冲流
System.out.println("缓冲字节流读取:"+bfit.read());
// 创建字节输出缓冲流,也可以进行续写的选择
BufferedOutputStream bfot1=new BufferedOutputStream(new FileOutputStream(f));//不续写
BufferedOutputStream bfot2=new BufferedOutputStream(new FileOutputStream(f,true));//续写
bfot1.write(97);
bfot1.close();
bfot2.write(99);
bfot2.close();
2.字符缓冲流
// 字符缓冲流:由于字符流自身默认在内存中带一个缓冲区(8192字节),所以字符缓冲流有两个缓冲区(即16384字节),
// 虽然其对读取效率提升不太明显,但是其有两个常用的方法比较重要:
// 1.每次读取一行 readLine();
// 2.写入跨系统换行符 newLine();
System.out.println();
File f= new File("File_Experiment\\aaa\\aaa.txt");
BufferedReader bfr=new BufferedReader(new FileReader(f));//创建一个字符输入缓冲流
int b;
System.out.print("BufferedReader:");
System.out.println("一次读取一行:"+bfr.readLine());//一次读取一行数据(以换行符为一行的结尾),且换行符不会读取进来
while((b=bfr.read())!=-1){
System.out.print(b+" ");
}
bfr.close();
System.out.println();
// 创建字符输出缓冲流
BufferedWriter bfw1=new BufferedWriter(new FileWriter(f));//不续写
BufferedWriter bfw2=new BufferedWriter(new FileWriter(f,true));//续写
bfw1.write(97);
bfw2.newLine();//跨系统换行符
bfw2.write("字符缓冲输出流测试");
bfw1.close();
bfw2.close();
2.转换流
// 转换流:作为字节流的高级类,其底层依旧是使用字节流进行传输数据,所以用法和字节流差不多,但其作为包装的高级类,
// 其作用是使得字节流也能够使用字符流的一些方法,例如不受字符编码的限制可以指定编码格式来进行读取文件、一次读取一行文件、写入跨系统的换行符等。
File f=new File("File_Experiment\\temp_1\\FileTemp.txt");
InputStreamReader isr=new InputStreamReader(new FileInputStream(f), Charset.forName("GBK"));//创建输入字节转换流,指定GBK格式(默认位UTF-8)
BufferedReader bfi=new BufferedReader(isr);
System.out.println("转换流一次读取一行:"+bfi.readLine());
bfi.close();
int i;
System.out.println("转换流读取GBK格式文件:"+isr.read());
while((i= isr.read())!=-1){
System.out.print((char) i);
}
isr.close();
// 创建输出字节续写流
OutputStreamWriter ost_1=new OutputStreamWriter(new FileOutputStream(f),Charset.forName("GBK"));//不续写
OutputStreamWriter ost_2=new OutputStreamWriter(new FileOutputStream(f,true),Charset.forName("GBK"));//续写
String s="ab\n我本将心比明月,奈何明月照沟渠";
ost_1.write(s);
ost_2.write("\n天生我才必有用,千金散尽还复来");
ost_1.close();
ost_2.close();
System.out.println();
3.序列化流
// 序列化流:可以将一个对象存入文件中(自动加密,文件一旦存入则不可修改,否则无法读取),作为要存入的对象类需要先继承一个标记型接口Serializable,
// 此接口无方法需重写,仅作为标记使用,代表实现了此接口的类可以被序列化,如果存入对象之后对类进行了修该也会导致无法读取,因为此方法内部会对
// serialVersionUID进行检查,不一致则无法读取,serialVersionUID是Java根据类的变量和方法自动生成的一个long类型的数值,如果不想每次修改之后就
// 无法读取可以在类中固定版本号private static final long serialVersionUID= xxxxL; 注意变量名必须为serialVersionUID否则不起作用,对于一
// 次性要序列化n个对象可以先将所有要序列化的对象都存入一个列表中,再对列表进行序列化,由于列表ArrayList底层已经固定了版本号所以不用担心版本号不匹配,
// 且读取的时候也可以对列表进行遍历(对于读取,如果读取完所有内容依旧读取的话则报错,使用列表就可以做到对不清楚具体有多少对象的文件读取不报错且全部读取)。
File f=new File("File_Experiment\\aaa\\aaa.txt");
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(f));
tempStream t_1=new tempStream(12,"小A","上海");
tempStream t_2=new tempStream(14,"小B","北京");
tempStream t_3=new tempStream(22,"小C","深圳");
ArrayList<tempStream> lts=new ArrayList<>();
lts.add(t_1);
lts.add(t_2);
lts.add(t_3);
oos.writeObject(lts);
oos.close();
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(f));
ArrayList<tempStream> ls=(ArrayList<tempStream>)ois.readObject();
ois.close();
for (tempStream o : ls) {
System.out.println(o);
}
// 创建继承Serializable接口的类
class tempStream implements Serializable {
// 固定序列化号
// private static final long serial=-129374152718L;
int age;
String name;
String locate;
public tempStream(FileOutputStream os) {
}
public tempStream(int age, String name, String locate) {
this.age = age;
this.name = name;
this.locate = locate;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return locate
*/
public String getLocate() {
return locate;
}
/**
* 设置
* @param locate
*/
public void setLocate(String locate) {
this.locate = locate;
}
public String toString() {
return "tempStream{age = " + age + ", name = " + name + ", locate = " + locate + "}";
}
}
4.打印流
// 打印流:分为字符打印流和字节打印流,作用为使写入的数据原样写入,不会使数据改变为二进制编码
// System.out.println/print/printf()也是使用打印流来将数据打印至控制台(本质是使用链式编程,先创建一个System.out的对象再调用print/printf/println)
File f=new File("File_Experiment\\aaa\\aaa.txt");
FileOutputStream os=new FileOutputStream(f,true);
FileOutputStream os2=new FileOutputStream(f);
PrintStream pos_1=new PrintStream(os2);
pos_1.println("苍天已死");
pos_1.print("黄天当立");
pos_1.printf("%s在甲子,%c下大吉","岁",'天');
pos_1.close();
PrintStream pos_2=new PrintStream(os,true,"GBK");
pos_2.print("苍天&已死");
pos_2.println("黄天&当立");
pos_2.printf("%s在甲子,%c下大吉","岁",'天');
pos_2.close();
FileWriter fw=new FileWriter(f,true);
PrintWriter pw_1=new PrintWriter(fw);
pw_1.println("先天下之忧而忧");
pw_1.print("后天下之乐而乐");
pw_1.printf("%s在甲子***%c下大吉","岁",'天');
pw_1.close();
PrintWriter pw_2=new PrintWriter(fw,true);
pw_2.println("居庙堂之高而忧其民");
pw_2.println("处江湖之远则忧其君");
pw_2.close();
PrintStream ps=System.out;
ps.println("测试输出1");
ps.close();
System.out.println("测试输出2");
5.压缩流
压缩流顾名思义就是用于对文件进行压缩和解压的包装类,在Java中IO流一般只对zip文件进行操作,即将文件压缩为zip文件或将zip文件解压;
文件压缩流程:
文件的压缩一般要有两个File类(待压缩的文件存放路径,压缩后的文件存放路径),然后创建一个压缩流(ZipOutputStream)用于压缩文件,在创建一个ZipEntry对象将待压缩文件的文件名放入其中,再ZipEntry对象放入ZipOutputStream对象的putNextEntry中,再创建一个FileInputStream对象对待压缩文件内容进行遍历读取,将读取到的每个字节都往压缩流中写入即ZipOutputStream对象的write(content),最后关闭FileInputStream、关闭压缩流中的entry以及关闭压缩流
对于单个文件的压缩
File f=new File("E:\\javaDM\\File_Experiment","temp_1.zip");//文件压缩后的文件路径
File f_1=new File("E:\\javaDM\\File_Experiment\\temp_1\\copy.jpg");//待压缩文件的文件路径
ZipOutputStream zop=new ZipOutputStream(new FileOutputStream(f));//创建压缩流对象,参数为压缩文件路径的Output流
ZipEntry ze=new ZipEntry(f_1.getName());//创建一个entry对象用于存放压缩的文件
zop.putNextEntry(ze);//将entry对象放入压缩流
FileInputStream fip=new FileInputStream(f_1);//创建一个Output流,用于复制文件
// 遍历文件
int i;
while ((i=fip.read())!=-1){
zop.write(i);
}
fip.close();//关闭Output流
zop.closeEntry();//关闭压缩流中的entry
zop.close();//关闭压缩流
对于文件夹的压缩
private static void compression() throws IOException{
File f=new File("E:\\javaDM\\File_Experiment","temp_1.zip");//文件待压缩后的文件路径
File f_1=new File("E:\\javaDM\\File_Experiment\\temp_1");//待压缩的文件路径
ZipOutputStream zop=new ZipOutputStream(new FileOutputStream(f));//创建压缩流对象,参数为压缩文件路径的Output流
compressionBl(f_1,zop,f_1.getName());//压缩一个文件夹
zop.close();//关闭压缩流
}
private static void compressionBl( File f_1,ZipOutputStream zop,String Name) throws IOException {
// 注意:压缩文件夹需使用递归进行,所以压缩流不能在此方法中关闭,但在调用者方法中必须关闭压缩流,否则会导致压缩文件异常
File[] fs=f_1.listFiles();//将压缩文件中的所有文件目录取出存入列表中
for (File file : fs) {//循环列表
if (file.isFile()){//判断是否为文件,是文件则按上面解压单个文件流程压缩
ZipEntry ze=new ZipEntry(Name+"\\"+file.getName());
zop.putNextEntry(ze);
FileInputStream fip=new FileInputStream(file);
int i;
while ((i=fip.read())!=-1){
zop.write(i);
}
zop.closeEntry();//关闭压缩流中的entry,以进入下一次循环
fip.close();//关闭Output流
}else{//如果是文件夹则递归
compressionBl(file,zop,Name+"\\"+file.getName());
}
}
}
文件解压流程
对于文件的解压一般也是先创建两个file对象(一个是待解压文件的路径,一个是文件解压后的文件路径),再创建一个ZipInputStream 解压缩对象将FileInputStream(文件路径为待解压缩的文件路径)对象传入其构造方法,然后再创建一个ZipEntry对象用于存放获取的压缩文件中的文件,然后用ZipInputStream对象的getNextEntry()方法(此方法每调用一次就取出压缩文件中的一个文件,若全部文件读取文本则返回null)配合循环来依次获取压缩文件中的每一个文件,在循环中需对获取的文件进行判断,如果是一个文件夹则再ZipEntry对象中创建一个文件夹,如果是一个文件则将文件复制至指定的文件位置
文件解压
File f_1=new File("File_Experiment\\aaa.zip");//待解压的文件路径
File f_2=new File("File_Experiment\\");//解压后的文件存放位置
ZipInputStream zp_1=new ZipInputStream(new FileInputStream(f_1));//创建一个解压缩对象,传入压缩文件路径的Input流
// ZipEntry ze=zp_1.getNextEntry();//获取压缩文件中的一个文件(从压缩文件夹开始,对内部文件依次获取直到将文件全部取出,如果没有能再读取的文件则返回null)
// System.out.println("--------"+ze.toString());
ZipEntry ze;//创建一个entry对象用于存放获取的压缩文件中的文件
while ((ze=zp_1.getNextEntry())!=null){
if (ze.isDirectory()){//判断获取的文件是否为文件夹,如果是文件夹则创建一个文件夹
File f=new File(f_2,ze.toString());
f.mkdirs();
}else {//如果是文件的话则复制文件内容至指定解压文件夹的相应位置
FileOutputStream fi=new FileOutputStream(new File(f_2,ze.toString()));
int i;
while ((i=zp_1.read())!=-1){
fi.write(i);
}
fi.close();//关闭写入流
zp_1.closeEntry();//关闭解压流中的entry,以进入下一次循环
}
}