您的位置:首页 > 编程语言 > Java开发

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

2014-07-25 11:18 811 查看

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编码。

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();
}
}

乱码产生的原因之一:

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


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐