12. JAVA IO Part 3 (数据操作流、合并流、压缩流、回退流、字符编码) ----- 学习笔记
2014-07-25 11:18
811 查看
12.11 数据操作流
在IO包中,提供了两个与平台无关的数据操作流,分别为数据输出流(DataOutputStream)和数据输入流(DataInputStream)。通常数据输出流会按照一定的格式将数据输出,再通过数据输入流按照一定的格式将数据读入,这样可以方便地对数据进行处理。例如,有下表所示的一组表示订单的数据
商品名称 | 商品价格 | 商品数量 |
衬衣 | 98.3 | 3 |
手套 | 30.3 | 2 |
围巾 | 50.5 | 1 |
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编码。ISO8859-1: 属于单字节编码,最多只能表示0~255 的字符范围。 主要在英文上应用。
GBK/GB2312: 中文的国际编码,专门用来表示汉字。;是双字节编码。如果在此编码中出现中文,则使用IOS8859-1编码,GBK可以表示简体中文和繁体中文;而GBK2312 只能表示简体中文;GBK兼容GB2312
unicode: Java中使用此编码方法,是最标准的一种编码,使用十六进制表示编码。 但是此编码不兼容IOS8859-1编码
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(); } }
乱码产生的原因之一:
输出内容的编码(例如:程序指定)与接收内容的编码(如本机环境默认)不一致!!!!!!
相关文章推荐
- java IO(三):数据操作流、合并流、回退流、对象序列化、字符编码问题、压缩流
- 12. JAVA IO Party 2 (内存操作流、管道流、打印流、System类对IO的支持) ----- 学习笔记
- 12. JAVA IO Part 4 (对象的序列化) ----- 学习笔记
- Java基础系列10:内存操作流,管道流,合并流,压缩流以及回退流
- Java IO学习笔记(五):内存操作流
- 【JAVA IO】_数据操作流笔记
- java中的IO整理(3)----数据操作流---合并流--压缩流--输入输出重定向 - 老秋的日志 - 网易博客
- JAVA高级视频_IO输入与输出 Java程序与其他进程的数据通讯 学习笔记
- 【JavaSE学习笔记】IO流06_其他常用流(基本数据类型流、内存操作流、打印流、标准流、随机访问流、合并流、序列化与反序列化流)、属性集合类Properties
- 12. JAVA IO Party 1 (目标、File类、RandomAccessFile类、字节流和字符流、转换流) ----- 学习笔记
- Java之学习笔记(12)-----------数据类型转换valueOf、subString、indexOf用法
- 我的java学习笔记(12)关于反射(part 1)
- Java学习笔记之IO(六):字节流对中文数据的读写
- 重踏学习Java路上_Day22(数据操作流,内存操作流,打印流,标准输入输出流,随机访问流,合并流,序列化流,Properties,NIO)
- 【JAVA IO】_数据操作流笔记
- 《SQL Server 2005数据挖掘与商业智能完全解决方案》学习笔记(4/12)(Part 1)
- 传智博客学习笔记12--JAVA IO
- Java学习笔记12
- java学习笔记三(Java数据类型)
- java学习笔记IO