12. JAVA IO Part 3 (数据操作流、合并流、压缩流、回退流、字符编码) ----- 学习笔记

12.11 数据操作流

          在IO包中,提供了两个与平台无关的数据操作流,分别为数据输出流(DataOutputStream)和数据输入流(DataInputStream)。通常数据输出流会按照一定的格式将数据输出,再通过数据输入流按照一定的格式将数据读入,这样可以方便地对数据进行处理。

    例如,有下表所示的一组表示订单的数据

商品名称商品价格商品数量
衬衣98.33
手套30.32
围巾50.51

     如果要将以上数据保存到文件中,就可以使用数据输出流将内容保存到文件,然后再使用数据输入流从文件中读取进来。

       12.11.1 DataOutputStream类

 类DataOutputStream是OutputStream类的子类。此类的定义如下:

public class DataOutputStream extends FilterOutputStream implements DataOutput

此类继承自FilterOutputStream类(FilterOutputStream类是OutputStream类的子类),同时实现了DataOutput接口,在DataOutput接口定义了一系列的写入各种数据的方法。

  • DataOutput是数据的输出接口,其中定义了各种数据的输出操作方法。例如在DataOutputStream类中的各种writeXxx()方法就是此接口定义的,但是在数据输出时一般都会直接使用DataOutputStream类,只有在对象序列化时才有可能直接操作到此接口。这点可以在Externalizable接口时可以看到!!


范例:将订单数据写入到文件order.txt中。

package org.forfan06.datademo;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class DataOutputStreamDemo01{
    public static void main(String args[]){
        DataOutputStream dos = null;   //声明数据输出流对象
        //Step 1: 指定操作文件的位置 (指定文件的保存路径)
        File f = new File("E:" + File.separator + "order.txt");
        //Step 2: 通过子类实例化对象,实例化数据输出流对象
        dos = new DataOutputStream(new FileOutputStream(f)); 
        //new DataOutputStream(OutputStream out); 
        //OutputStream out = new FileOutputStream(f);
        String names[] = {"衬衣", "手套", "围巾"};
        float prices[] = {98.3f, 30.3f, 50.5f};
        int nums[] = {3, 2, 1};
        //Step 3: 输出输入操作
        for(int i = 0; i < names.length; i++){
            dos.writeChars(names[i]);
            dos.writeChar('\t');
            dos.writeFloat(prices[i]);
            dos.writeChar('\t');
            dos.writeInt(nums[i]);
            dos.writeChar('\n');
        }
        //Step 4: 关闭流
        dos.close();   //关闭输出流
    }
}

       12.11.2 DataInputStream类

DataInputStream类是InputStream类的子类,专门负责读取使用DataOutputStream类输出的数据。此类的定义如下:

public class DataInputStream extends FilterInputStream implements DataInput

DataInputStream类继承自FilterInputStream类(FilterInputStream类是InputStream类的子类);同时实现了DataInput接口,在DataInput接口中定义了一些列读入各种数据的方法。

  • DataInput接口是读取数据的操作接口,与DataOutput接口提供的各种writeXxx()方法对应,在此接口中定义了一系列的readXxx()方法,这些方法在DataInputStream类中都有实现。一般在操作时不会直接使用到此接口,而主要是用DataInputStream类完成读取功能,只有在对象序列化时才有可能直接利用此接口读取数据。这点可以在Externalizable接口时可以看到!!


范例:从order.txt中读取数据

package org.forfan06.datademo;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
public class DataInputStreamDemo01{
    public static void main(String args[]) throws Exception{
        DataInputStream dis = null;
        //Step 1:
        File f = new File("E:" + File.separator + "order.txt");
        //Step 2:
        dis = new DataInputStream(new FileInputStream(f));
        //InputStream is = new FileInputStream(f);
        //dis = new DataInputStream(is);
        
        String name = null;
        float price = 0.0f;
        int num = 0;
        char temp[] = null;
        char c = 0;
        int len = 0;
        
        //Step 3:
        try{
            while(true){
                temp = new char[200];
                len = 0;
                while((c = dis.readChar()) != '\t'){
                    temp[len] = c;
                    len++;
                }
                name = new String(temp, 0, len);
                price = dis.readFloat();
                dis.readChar();  //读出\t
                num = dis.readInt();
                dis.readChar();
                System.out.printf("名称:%s; 价格:%5.2f; 数量:%d\n", name, price, num);
            }
        }catch(Exception e){  //如果读到底,则会出现异常
            
        }
        //Step 4:
        dis.close();
    }
}

12.12 合并流

合并流的主要功能是将两个文件的内容合并成为一个文件。

如果要实现合并流,则必须使用SequenceInputStream类。此类的常用方法有:

public SequenceInputStream(InputStream s1, InputStream s2)       //使用两个输入流对象实例化本类对象
public int available() throws IOException   //返回文件大小

范例:合并两个文件

package org.forfan06.sequencedemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
public class SequenceDemo01{
    public static void main(String args[]) throws Exception{
        InputStream is1 = null;
        InputStream is2 = null;   //输入流1,2
        OutputStream os = null;  //输出流
        SequenceInputStream sis = null;   //合并流
        
        is1 = new FileInputStream("E:" + File.separator + "a.txt");
        is2 = new FileInputStream("E:" + File.separator + "b.txt");
        os = new FileOutputStream("E:" + File.separator + "ab.txt");
        sis = new SequenceInputStream(is1, is2); //实例化合并流
        
        int temp = 0;
        while((temp = sis.read()) != -1){
            os.write(temp);
        }
        sis.close();
        is1.close();
        is2.close();
        os.close();
    }
}

SequenceInputStream类在进行读取时, 实际上是从两个输入流中一起读取内容的。

12.13 压缩流

        在Java中为了减少传输时的数据量,提供了专门的压缩流,可以将文件或文件夹压缩成ZIP、JAR、GZIP等文件形式。

       12.13.1 ZIP压缩输入/输出简介

       ZIP是一种很常见的压缩形式,在Java中要实现ZIP的压缩需要导入java.util.zip包。可以使用此包中的ZipFile、ZipOutputStream、ZipInputStream、ZipEntry几种类完成操作。

(1)ZIP压缩的支持类保存在java.util.zip包中。常用类有如下几种:

  • Zip压缩输出流: ZipOutputStream
  • Zip压缩输入流: ZipIntputStream
  • Zip文件:  ZipFile
  • Zip实体:  ZipEntry

(2)JAR压缩的支持类保存在java.util.jar包中,常用类有如下几种:

  • JAR压缩输出流: JarOutputStream
  • JAR压缩输入流: JarInputStream
  • JAR文件:JARFile
  • JAR实体: JAREntry

(3)GZIP是用于UNIX系统的文件压缩,在Linux中经常会使用到*.gz的文件,就是GZIP格式;GZIP压缩的支持类保存在java.util.zip包中,常用类有如下两个:

  • GZIP压缩输出流:GZIPOutputStream
  • GZIP压缩输入流: GZIPInputStream

在每一个压缩文件中都会存在多个字文件,那么每一个字文件在Java中就是用ZipEntry表示。

ZipEntry类的常用方法:

public ZipEntry(String name)        //创建对象并指定要创建的ZipEntry名称
public boolean isDirectory()   //判断此ZipEntry是否是目录

  • 压缩的输入类/输出类定义在java.util.zip包中。 压缩的输入/输出流也属于InputStream类或OutputStream类得子类。但是却没有定义在java.io包中,而是以一种工具类得形式提供的,在操作时还需要使用java.io包的支持

       12.13.2 ZipOutputStream类

如果要完成一个文件或文件夹的压缩,则要使用ZipOutputStream类。ZipOutputStream类是OutputStream类的子类,常用操作方法如下:

public ZipOutputStream(OutputStream out)    //构造方法,创建新的ZIP输出流
public void putNextEntry(ZipEntry e) throws IOException     //设置每一个ZipEntry对象
public void setComment(String comment)    //设置ZIP文件的注释

(1)压缩文件

范例:在E盘中存在一个temp.txt文件,要将其压缩成temp.zip文件

package org.forfan06.zipdemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipOutputStreamDemo01{
    public static void main(String args[]) throws Exception{
        //Step 1:
        File file = new File("E:" + File.separator + "temp.txt");
        
        File zipFile = new File("E:" + F.separator + "temp.zip");
        //Step 2:
        InputStream input = new FileInputStream(file);
        
        ZipOutputStream zipOut = null;
        zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
        
        zipOut.putNexEntry(new ZipEntry(file.getName)));
        zipOut.setComment("@author forfan06");
        //Step 3:
        int temp = 0;
        while((temp = input.read()) != -1){
            zipOut.write(temp);
        }
        //Step 4:
        input.close();
        zipOut.close();
    }
}

以上程序将temp.txt作为源文件,然后使用ZipOutputStream类将所有的压缩数据输出到temp.zip文件中,在压缩时同样采用了边读边写的方式完成:

while((temp = input.read()) != -1){

     zipOut.write(temp);

}

程序运行后,会在E盘创建一个temp.zip的压缩文件。


(2)压缩文件夹

现在要压缩E盘中的文件夹Temp文件夹。

分析:  如果现在要进行压缩,则在压缩后的文件中应该存在一个Temp文件夹。在文件夹中应该存放着各个压缩文件。 所以在实现时就应该列出文件夹中的全部内容,并把每一个内容设置成ZipEntry对象,保存到压缩文件中,执行流程如下所示:

      


范例: 压缩一个文件夹

package org.forfan06.zipdemp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipOutputStreamDemo02{
    public static void main(String args[]) throws Exception{
        //Step 1:
        File file = new File("E:" + File.separator + "Temp");
        
        File zipFile = new File("E:" + File.separator + "Temp.zip");
        
        InputStream input = null;
        ZipOutputStream zipOut = null;
        //Step 2: 
        zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
        zipOut.setComment("zip a folder!!!!!!!!!");
        
        //Step 3:
        if(file.isDirectory()){
            File lists[] = file.listFiles();  //列出所有文件
            for(int i = 0; i < lists.length; i++){
                input = new FileInputStream(lists[i]); //设置文件输入流
                //每一个被压缩的文件都用ZipEntry表示,需要为每一个压缩后的文件设置名称
                zipOut.putNextEntry(new ZipEntry(file.getName() + File.separator + lists[i].getName())); //创建ZipEntry
                int temp = 0;
                while((temp = input.read()) != -1){
                    zipOut.write(temp);
                }
                input.close();
            }
        }
        //Step 4: 
        zipOut.close();
    }
}

程序首先判断给定的路径是否是文件夹,如果是文件夹,则将此文件夹中的内容使用listFiles()方法全部列出来,此方法返回File类的对象数组,然后将此File对象数组中的每一个文件进行压缩,每次压缩时都要设置一个新的ZipEntry对象。

       12.13.3 ZipFile类

   在Java中,每一个压缩文件都可以使用ZipFile类表示,还可以使用ZipFile类根据压缩后的文件找到每一个压缩文件中的ZipEntry并将其进行解压缩操作。

ZipFile类的常用方法如下:

ZipFile类实例化时,需要File指定的路径。下面介绍ZipFile类的基本使用。

范例:实例化ZipFile类对象

package org.forfan06.zipdemo;
import java.io.File;
import java.util.zip.ZipFile;
public class ZipFileDemo01{
    public static void main(String args[]) throws Exception{
        File file = new File("E:" + File.separator + "forfan06.zip");  //定位压缩文件
        ZipFile zipFile = new ZipFile(file);  //实例化ZipFile类的对象
        System.out.println("压缩实体名称:" + zipFile.getName()); //得到压缩文件的名称
    }
}

下面来利用ZipFile类进行文件的解压缩操作。

范例:解压缩文件

package org.forfan06.zipdemo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/*
 *E盘下有一个forfan06.zip文件。里面只包含一个forfan06.txt文件。
 */
public class ZipFileDemo02{
    public static void main(String args[]) throws Exception{
        //Step 1:
        File file = new File("E:" + File.separator + "forfan06.zip");
        //Step 2:
        File outputFile = new File("E:" + File.separator + "forfan06_upzip.txt"); //定义解压缩的文件名称
        ZipFile zipFile = new ZipFile(file); //实例化ZipFile对象
        //Step 3:
        ZipEntry entry = zipFile.getEntry("forfan06.txt"); //得到一个压缩实体 
        InputStream input = zipFile.getInputStream(entry);  //取得ZipEntry输入流
        OutputStream out = new FileOutputStream(outputFile); //实例化输出流对象
        
        int temp = 0;  //保存接收数据
        while((temp = input.read()) != -1){ //读取数据
            out.write(temp);  //输出数据
        }
        //Step 4:
        input.close();
        out.close();
    }
}
       以上程序是将E盘中的forfan06.zip中的文件解压缩到forfan06_uzip.txt文件中。程序首先通过getEntry()方法根据名称取得一个压缩的ZipEntry;然后通过InputStream取得此ZipEntry的输入流。并通过循环的方式将全部内容通过输出流输出。

       但是,以上程序只适合于压缩文件中只有一个ZipEntry的情况(并且还需要知道文件名。。。)!!!如果一个压缩文件中有多个文件夹或者多个ZipEntry就无法使用了。如果要操作更加复杂的压缩文件,就必须结合ZipInputStream类完成!!!!

       12.13.4 ZipInputStream类

       ZipInputStream类是InputStream类的一个子类。通过此类就可以方便地读取ZIP格式的压缩文件。此类的常用方法如下所示:

使用ZipInputStream类可以像ZipFile一样取得Zip压缩文件中的每一个ZipEntry!!!

范例:取得forfan06.zip中的一个ZipEntry

package org.forfan06.zipdemo;
import java.io.File;
import java.io.FileInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipInputStreamDemo01{
    public static void main(String args[]) throws Exception{
        File file = new File("E:" + File.separator + "forfan06.zip"); //定位压缩文件
        ZipInputStream input = null;  //定义压缩输入流
        input = new ZipInputStream(new FileInputStream(file));  //实例化压缩输入流
        
        ZipEntry entry = input.getNextEntry();  //取得压缩实体
        System.out.println("压缩实体名称:" + entry.getName());
        
        input.close();
    }
}

       通过ZipInputStream类中的getNextEntry()方法可以一次取得每一个ZipEntry,那么将ZipInputStream类与ZipFile类结合,就可以对压缩的文件夹进行解压缩操作。但是需要注意的是:在forfan06.zip文件本身是包含压缩的文件夹。所以在进行解压缩之前,应该先根据ZIP文件中的文件夹的名称在硬盘上创建好一个对应的文件夹,然后才可以把文件解压缩进行;而且在操作时对于每一个解压缩的文件都必须先创建(利用File类的createNewFile()方法来创建新文件)后再将内容输出!!!!

范例:解压缩包含多个ZipEntry的文件

package org.forfan06.zipdemo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipEntry;
public class ZipInputStreamDemo02{
    public static void main(String args[]) throws Exception{
        File file = new File("E:" + File.separator + "forfan06.zip");
        
        File outFile = null;  //定义输出的文件对象
        ZipFile zipFile = new ZipFile(file);
        ZipInputStream zipInput = new ZipInputStream(new FileInputStream(file));
        
        ZipEntry entry = null;  //用于接收压缩文件中的每一个实体      
        InputStream input = null;  //输入流,用于读取每一个ZipEntry
        OutputStream out = null;  //输出流,用于输出每一个实体内容
        while((entry = zipInput.getNextEntry()) != null){
            System.out.println("解压缩:" + entry.getName() + "文件!");
            outFile = new File("E:" + File.separator + entry.getName());
            
            if(!outFile.getParentFile().exists()){
                outFile.getParentFile().mkdir();
            }
            if(!outFile.exists()){
                outFile.createNewFile();
            }
            
            input = zipFile.getInputStream(entry);
            out = new FileOutputStream(outFile);
            
            int temp = 0;
            while((temp = input.read()) != -1){
                out.write(temp);
            }
            
            //应该在这里关闭输入/输出流吗?
            input.close();
            out.close();
        }
    }
}

      解析:上面程序首先使用ZipInputStream类读取ZIP格式的压缩文件,然后通过getNextEntry()方法依次读取出其中的每一个ZipEntry对象的名称;并通过ZipFile类取得每一个ZipEntry的输入流对象!在进行文件输出前,判断其输出的文件夹和文件是否存在,如果不存在则创建!!!

12.14 回退流

       在JavaIO中,所有的数据都是采用顺序的读取方式!!即对于一个输入流来说,都是采用从头到尾的顺序读取的。 那么现在,如果在输入流中有某些不需要的内容,要想处理掉这些不需要的内容,则只能通过程序将这些不需要的内容处理掉。

       为了处理输入流中不需要的内容,在Java中提供了一种回退输入流(PushbackInputStream、PushbackReader),可以把读取进来的某些数据重新退回到输入流的缓冲区中。

      在回退流中,对于不需要的数据可以使用unread()方法将内容重新送回到输入流的缓冲区中。 下面以PushbackInputStream类为例进行讲解。

上表中的3个unread()方法与InputStream(PushbackupInputStream类事InputStream类的一个子类!!)类中的3个read()方法相对应。所以回退完全是针对于输入流进行的操作。

范例:内存中有“www.csdn.net”字符串,只要输入的内容是“.”,则执行回退操作。也就是不读取“.”

package org.forfan06.pushbackdemo;
import java.io.ByteArrayInputStream;
import java.io.PushbackInputStream;
public class PushbackInputStreamDemo01{
    public static void main(String args[]) throws Exception{
        //Step 1:
        String str = "www.csdn.net";
        PushbackInputStream push = null;
        ByteArrayInputStream byteInput = null;
        //Step 2:
        byteInput = new ByteArrayInputStream(str.getBytes());
        push = new PushbackInputStream(byteInput);
        //Step 3:
        System.out.print("读取之后的数据为:");
        int temp = 0;
        while((temp = push.read()) != -1){
            if(temp == '.'){
                push.unread(temp);
                temp = push.read();
                System.out.print("{退回" + (char)temp + ")");
            }else{
                System.out.print((char) temp);
            }
        }
        //Step 4:
        push.close();
        byteInput.close();
    }
}

解析:

while((temp = push.read()) != -1){
 if(temp == *.*){
 push.unread(temp); //这里把 . 给 push back到缓冲区前面
temp = push.read(); //紧接着,这里有把上面push back回去的 . 又给读出来了!!!
}
 //有没有都没有关系
}
这里没有死循环,当判断是 “.” 时 ,把这个 “.” push back 回去了。但是在这个if语句中紧接着又把这个 “.”通过 temp = push.read()给读出来了。。。。
然后这个if语句执行完了。跳到外面继续执行while, 此时temp = push.read()这里应该是紧接着 上面if中的那个 . 之后的字符了

==================copy from zhidao baidu===================

你要了解用回退流做什么。。。
回退就是为了下次读取的时候再读回来。。
比如<aaa chartset='utf8'>   解析输入流时  判断chartset='utf8'时  按照utf8解析。。 但是输入流读到utf8的时候前面的字符已经读过了。read()智能继续往下读。这时可以把之前读过的都回搜索退。。
重新读所有的字符。。。。。
大概就是这么个用法吧。。

==================copy from zhidao baidu===================

12.15 字符编码

       12.15.1 Java常见编码简介

      在计算机里,任何的文字都是以制定的编码方式存在的。在Java程序的开发中,最常见的编码方式是:ISO8859-1、GBK/GB2312、unicode、UTF编码。

  1. ISO8859-1: 属于单字节编码,最多只能表示0~255 的字符范围。  主要在英文上应用。
  2. GBK/GB2312: 中文的国际编码,专门用来表示汉字。;是双字节编码。如果在此编码中出现中文,则使用IOS8859-1编码,GBK可以表示简体中文和繁体中文;而GBK2312 只能表示简体中文;GBK兼容GB2312
  3. unicode: Java中使用此编码方法,是最标准的一种编码,使用十六进制表示编码。 但是此编码不兼容IOS8859-1编码
  4. UTF: 由于unicode不支持ISO8859-1编码,而且容易占用更多的空间,而且对于英文字母也需要使用两个字节编码,这样使用unicode不便于传输和存储。因为产生了UTF编码。  UTF编码兼容了ISO8859-1编码,同时也可以用来表示所有的语言自负,不过UTF编码是不定长编码,每一个字符的长度为1~6个字节不等。  一般在中文网页使用此编码,可以节省空间

            在程序中如果处理不好字符的编码,则就有可能出现乱码问题。如果本机的默认编码是GBK,但在程序中使用了ISO8859-1编码,则会出现字符的乱码!

如果要避免产生乱码,则程序的编码与本地的默认编码保持一致即可。而本地的默认编码,可以使用System类来获得!!!!

       12.15.2 得到本机的编码显示

        获取系统的默认编码。需要使用到System类!!!!

public static Properties getProperty()

范例:使用System类得到JVM的默认编码

package org.forfan06.charsetdemo;
public class CharSetDemo01{
    public static void main(String args[]){
        System.out.println("系统默认编码:" + System.getProperty("file.encoding"));
    }
}

       12.15.3 乱码产生

  假如,本地的默认编码是GBK;通过ISO8859-1编码对文字进行编码转换。

 如果要实现编码转换可以使用String类中的getBytes(String charset)方法,此方法可以设置指定的编码。其格式如下:

pubcli byte[] getBytes(String charset)
范例: 使用ISO8859-1编码

package org.forfan06.charsetdemo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class CharSetDemo02{
    public static void main(String args[]) throws Exception{
        File f = new File("E:" + File.separator + "test.txt");
        OutputStream out = new FileOutputStream(f);
        byte[] b = "陌生人,你好!".getBytes("ISO8859-1");
        out.write(b);
        out.close();
    }
}

乱码产生的原因之一:

    输出内容的编码(例如:程序指定)与接收内容的编码(如本机环境默认)不一致!!!!!!


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值