Sunday, April 26, 2009

位流

InputStream & OutputStream
InputStream是所有表示位输入流的类的父类,它是一个抽象类,继承它的子类需要重新定义其中所定义的抽象方法。InputStream是从装置来源地读取数据的抽象表示
例如System中的标准输入流in对象就是一个InputStream类型的实例。在Java程序开始之后,in流对象就会开启
同理OutputStream & 对应的out对象

例:in对象的read()方法一次读取一个字节的数据,读入的数据以int类型返回。所以在使用out对象将数据显示出来时,就是10进制方式
package 输入输出;
import java.io.*;
public class StreamDemo {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
System.out.print("输入字符:");
System.out.println("输入字符十进制表示:"+System.in.read());
}
catch(IOException e){
e.printStackTrace();
}

}

}
运行结果:
输入字符:A
输入字符十进制表示:65


FileInputStream & FileOutputStream
当建立一个FileInputStream 或FileOutputStream的实例时,必须指定文件位置及文件名称,实例被建立时文件的流就会开启;而不是用流时必须关闭文件流,以释放与流相依的系统资源,完成文件读/写的动作
FileInputStream可以使用read()方法一次读入一个字节,并以int类型返回,或者是使用read()方法时读入至一个byte数组(缓冲区)
例:复制文件
package 输入输出;
import java.io.*;
public class FileStreamDemo {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] fs=new String[2];
fs[0]="D:\\日本語\\二级\\2级语法\\2级语法190.doc";
fs[1]="test.doc";
try{
byte[] buffer=new byte[1024];
//来源文件
FileInputStream fileInputStream=new FileInputStream(new File(fs[0]));
//目的文件
FileOutputStream fileOutputStream=new FileOutputStream(new File(fs[1]));
//available()可取得未读取的数据长度
System.out.println("复制的文件:"+fileInputStream.available()+"字节");

while(true){
if(fileInputStream.available()<1024){
//剩余的数据比1024少,一位一位读出再写入目的文件
int remain=-1;
while((remain=fileInputStream.read())
!=-1){
fileOutputStream.write(remain);
}
break;
}
else{
//从来源文件读取数据至缓冲区
fileInputStream.read(buffer);
//将数组数据写入目的文件
fileOutputStream.write(buffer);
}
}
// 关闭流
fileInputStream.close();
fileOutputStream.close();
System.out.println("复制完成");
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("using:java FileStreamDemo src des");
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}



}

}

FileOutputStream默认会以新建文件的方式来开启流。
如果指定的文件名称已经存在,则原文件会被覆盖
以附加的模式来写入文件:
FileOutputStream fileOutputStream=new FileOutputStream(string,true);



BufferedInputStream & BufferedOutputStream
为InputStream&OutputStream对象增加缓冲区
构建BufferedInputStream实例时,需要给定一个InputStream类型的实例,实现BufferedInputStream时实际上最后是实现InputStream实例
so do BufferedOutputStream
BufferedInputStream的数据成员buf是一个位数组,默认为2048字节
BufferedOutputStream的数据成员buf是一个位数组,默认为512字节。当使用write()方法写入数据时,实际上会先将数据写至buf中,当buf已满时才会实现给定的OutputStream对象的write()方法,将buf数据写至目的地
例:使用BufferedInputStream & BufferedOutputStream实现文件复制
package 输入输出;
import java.io.*;
public class BufferdeStreamDemo {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] arg=new String[2];
arg[0]="D:\\za\\code.txt";
arg[1]="test.txt";
try{
byte[] data=new byte[1];

File srcFile=new File(arg[0]);
File desFile=new File(arg[1]);

BufferedInputStream bufferedInputStream=new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(new FileOutputStream(desFile));
System.out.println("复制文件:"+srcFile.length()+"字节");
while(bufferedInputStream.read(data)!=-1){
bufferedOutputStream.write(data);
}
//将缓冲区中的数据全部写出
bufferedOutputStream.flush();
//关闭流
bufferedInputStream.close();
bufferedOutputStream.close();

System.out.println("复制完成");
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("using:java UseFileStream src des");
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}

}

}



DataInputStream&DataOutputStream
提供一些对Java基本数据类型写入的方法,在写入或读出基本数据类型时,就不用担心不同平台间数据大小不同的问题
例:Member类
package 输入输出;

public class Member {
private String name;
private int age;
public Member(){

}
public Member(String name,int age){
this.name=name;
this.age=age;
}

public void setName(String name){
this.name=name;
}

public void setAge(int age){
this.age=age;
}

public String getName(){
return name;
}

public int getAge(){
return age;
}

}

例:将Member类实例的成员数据写入文件中,并在读入文件数据后,将这些数据还原为Member对象
package 输入输出;
import java.io.*;
public class DataStreamDemo {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Member[] members={
new Member("Justin",90),
new Member("momor",95),
new Member("Bush",88)
};

String[] arg=new String[2];
arg[0]="test.txt";
arg[1]="text.txt";

try{
DataOutputStream dataOutputStream=new DataOutputStream(new FileOutputStream(arg[0]));
for(Member member:members){
//写入UTF
dataOutputStream.writeUTF(member.getName());
//写入int数据
dataOutputStream.writeInt(member.getAge());
}
//读出所有数据至目的地
dataOutputStream.flush();
//关闭流
dataOutputStream.close();

DataInputStream dataInputStream=new DataInputStream(new FileInputStream(arg[0]));
//读出数据并还原为对象
for(int i=0;i<>
//读出UTF字符串
String name=dataInputStream.readUTF();
int score=dataInputStream.readInt();
members[i]=new Member(name,score);
}
//关闭流
dataInputStream.close();

//现实还原后的数据
for(Member member:members){
System.out.printf("%s\t%d%n", member.getName(),member.getAge());
}
}
catch(IOException e){
e.printStackTrace();
}
}

}

ObjectInputStream & ObjectOutputStream
如果要直接存储对象,定义该对象的类必须实现java.io.Serializable接口,尽管接口中没有规范任何必须实现的方法(一个标志)
例:User类
package 输入输出;
import java.io.Serializable;
public class User implements Serializable{
private static final long serialVersionUID=1l;

private String name;
private int number;

public User(){

}

public User(String name,int number){
this.name=name;
this.number=number;
}

public void setName(String name){
this.name=name;
}

public void setNumber(int number){
this.number=number;
}

public String getName(){
return name;
}

public int getNumber(){
return number;
}

}
注意到serialVersionUID,它代表了可序列化对象的版本,如果没有提供这个版本信息,则实现Serializable接口的类会自动依类名称、实现的接口、成员等来产生。如果是自动生成的,则下次更改User类,自动产生的serialVersionUID也会跟着变更,从文件读回对象时若两个对象的serialVersionUID不相同,就会丢出java.io.InvalidClassException。
在写入对象时用writeObject()方法
读出对象时使用readObject()方法,被读出的对象都是以Object的类型返回,所以必须将之转换为对象原来的类型,才能正确的实现被读回的对象
例:存储User对象至文件中,然后再将它读回并还原为User实例
package 输入输出;
import java.io.*;
import java.util.*;
public class ObjectStreamDemo {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] arg=new String[2];
arg[0]="test.txt";
//arg[1]="test2.txt";
User[] users={
new User("cater",101),
new User("justin",102)
};
writeObjectToFile(users,arg[0]);
try{
//读取文件数据
users=readObjectsFromFile(arg[0]);
//显示读回的对象
for(User user:users){
System.out.printf("%s\t%d%n",user.getName(),user.getNumber());
}
System.out.println();
users=new User[2];
users[0]=new User("momor",103);
users[1]=new User("becky",104);

//附加新对象至文件
appendObjectsToFile(users,arg[0]);

//读取文件数据
users=readObjectsFromFile(arg[0]);
//显示读回对象
for(User user:users){
System.out.printf("%s\t%d%n", user.getName(),user.getNumber());
}
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("没有指定文件名");
}
catch(FileNotFoundException e){
e.printStackTrace();
}

}
public static void writeObjectToFile(Object[] objs,String filename){
File file=new File(filename);
try{
ObjectOutputStream objOutputStream=new ObjectOutputStream(new FileOutputStream(file));
for(Object obj:objs){
//将对象写入文件
objOutputStream.writeObject(obj);
}
//关闭流
objOutputStream.close();
}
catch(IOException e){
e.printStackTrace();
}

}

//将制定文件中的对象数据读回
public static User[] readObjectsFromFile(String filename)
throws FileNotFoundException
{
File file=new File(filename);
//如果文件不存在就丢出异常
if(!file.exists())throw new FileNotFoundException();
//使用List先存储读回的对象
List list=new ArrayList();

try{
FileInputStream fileInputStream=new FileInputStream(file);
ObjectInputStream objInputStream=new ObjectInputStream(fileInputStream);

while(fileInputStream.available()>0){
list.add((User)objInputStream.readObject());
}
objInputStream.close();
}
catch(ClassNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}

User[] users=new User[list.size()];
return list.toArray(users);
}


//将对象附加至指定的文件之后
public static void appendObjectsToFile(Object[] objs,String filename)
throws FileNotFoundException{
File file=new File(filename);
//如果文件不存在则丢出异常
if(!file.exists())throw new FileNotFoundException();

try{
ObjectOutputStream objOutputStream=new ObjectOutputStream(new FileOutputStream(file,true)){
//如果要附加对象至文件后
//必须重新定义这个方法
protected void writeStreamHeader()throws IOException{}
};

for(Object obj:objs){
//将对象写入文件
objOutputStream.writeObject(obj);
}
objOutputStream.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}

注意:
在试图将对象附加至一个先前已写入对象的文件时,由于ObjectOutputStream在写入数据时,还会加上一个特别的流头(Stream Heade),所以在读取文件时会检查这个流头。如果一个文件中被多次附加对象,那么该文件中会有多个流头。这样读取检查时就会发现不一致,这会丢出java.io.StreamCorrupedException为了解决这个问题可以重新定义ObjectOutputStream的writeStreamHeader()方法,如果是以附加方式来写入对象,就不写入流头:
ObjectOutputStream objectOutputStream=new ObjectOutputStream(
new FileOutputStream(file,true)){
protected void writeStreamHeader()throws IOException{}
};



SequenceInputStream

若将一个文件分割为数个文件,再将之组合还原为一个文件,SequenceInputStream是首选
SequenceInputStream可以被看做是数个InputStream对象的组合
当一个Input对象的内容读取完毕后,它会读取下一个InputStream对象直到所有的InputStream对象都读取完毕为止
例:将指定文件分割,也可以将分割后的文件还原为一个文件
package 输入输出;
import java.io.*;
import java.util.*;
public class SequenceStreamDemo {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] arg=new String[3];
arg[0]="c";
arg[1]="2";
arg[2]="test.txt";

try{
//arg[0]指定分割(s)或连接(c)
switch(arg[0].charAt(0)){
case 's':
//arg[1]:每个分割文件的大小
int size=Integer.parseInt(arg[1]);
//arg[2]:指定要分割文件的名称
seperate(arg[2],size);
break;
case 'c':
//arg[1]指定要被组合的文件个数
int number=Integer.parseInt(arg[1]);
//arg[2]组合后的文件名
concatenate(arg[2],number);
break;
}

}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("using:java UseSequenceStream[-s/-c]"+
"(size/number) filename");
System.out.println("-s:分割文件\n-c:组合文件");
}
catch(IOException e){
e.printStackTrace();
}

}


//分割文件:
public static void seperate(String filename,int size)throws IOException{
FileInputStream fileInputStream=new FileInputStream(new File(filename));
BufferedInputStream bufInputStream=new BufferedInputStream(fileInputStream);
byte[] data=new byte[1];
int count=0;
//还原文件大小及指定分割的大小
//决定要分割为几个文件
if(fileInputStream.available()%size==0)
count=fileInputStream.available()/size;
else
count=fileInputStream.available()/size+1;

//开始进行分割
for(int i=0;i< num="0;" file="new" bufoutputstream="new" num="="> list=new ArrayList();

for(int i=0;i< file =" new"> iterator=list.iterator();
//SequenceInputStream需要一个Enumeration对象来构建
Enumeration enumation=new Enumeration(){
public boolean hasMoreElements(){
return iterator.hasNext();
}
public InputStream nextElement(){
return iterator.next();
}
};

//建立SequenceInputStream
//并使用BufferedInputStream
BufferedInputStream bufInputStream=new BufferedInputStream(
new SequenceInputStream(enumation),8192);

BufferedOutputStream bufOutputStream=
new BufferedOutputStream(
new FileOutputStream(filename),8192);

byte[] data=new byte[1];
//读取所有文件数据并写入目的文件
while(bufInputStream.read(data)!=-1){
bufOutputStream.write(data);
}

bufInputStream.close();
bufOutputStream.flush();
bufOutputStream.close();
System.out.println("组合"+number+"个文件 OK!" );
}

}


PrintStream
使用java.io.PrintStream可以自动进行字符转换的动作,默认会使用操作系统的编码来处理对应的字符转换动作
例:
package 输入输出;
import java.io.*;
public class PrintStreamDemo {

/**
* @param args
*/
public static void main(String[] args) throws FileNotFoundException{
// TODO Auto-generated method stub
PrintStream printStream = new PrintStream(new File("test2.txt"));
printStream.println(1);
printStream.close();
}

}



ByteArrayInputStream & ByteArrayOutputStream
ByteArrayInputStream & ByteArrayOutputStream 是将位数组当做流输入来源,输出目的地的类
例:打开一个有简单字符(ABCD……)的简单文本文件,在读取文件之后,指定文件中字符的位置来修改字符
package 输入输出;
import java.util.*;
import java.io.*;
public class ByteArrayStreamDemo {

public static void main(String[] args) {
String arg=new String("test2.txt");
// TODO Auto-generated method stub
try{
File file=new File(arg);
BufferedInputStream bufferedInputStream=
new BufferedInputStream(
new FileInputStream(file));
ByteArrayOutputStream arrayOutputStream=new ByteArrayOutputStream();
byte[] bytes=new byte[1];

//将文件内容写入位数组流
while(bufferedInputStream.read(bytes)!=-1){
arrayOutputStream.write(bytes);
}
arrayOutputStream.close();
bufferedInputStream.close();

//以字符方式显示位数组内容
bytes=arrayOutputStream.toByteArray();
for(int i=0;i< bytes.length;i++){
System.out.print((char)bytes[i]);
}
System.out.println();

//让用户输入位置与字符修改位数组内容
Scanner scanner=new Scanner(System.in);

System.out.print("输入修改位置:");
int pos=scanner.nextInt();
System.out.print("输入修改字符:");
//修改数组中对应的字符
bytes[pos-1]=(byte)scanner.next().charAt(0);
//将位数组内容存回文件
ByteArrayInputStream byteArrayInputStream=
new ByteArrayInputStream(bytes);
BufferedOutputStream bufOutputStream=
new BufferedOutputStream(new FileOutputStream(file));
byte[] tmp=new byte[1];
while(byteArrayInputStream.read(tmp)!=-1)
bufOutputStream.write(tmp);
byteArrayInputStream.close();
bufOutputStream.flush();
bufOutputStream.close();

}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("请指定文件名称");
}

catch(IOException e){
e.printStackTrace();
}

}

}
结果:
AAAAAAAAAAAAAAA
输入修改位置:1
输入修改字符:B
文件修改后:BAAAAAAAAAAAAAA



PushbackInputStream
拥有一个Pushback缓冲区,从PushbackInputStream读出数据后,只要Pushback缓冲区没有满。就可以使用unread()将数据推回流的前端
例:区分ASCII & BIG5
BIG5使用两个字符来表示一个中文字,而ASCII只是用一个字节来表示英文字符,
BIG5中文为了与ASCII兼容,低字节范围为0xA4~0xF9,高字节范围为0x40~0x7E以及0xA1~0xFE。存储时低字节先存,再存高字节,所以读取时只要读到字节是在
0xA4~0xF9就表示它可能是一个中文字的前半数据

例:

package 输入输出;
import java.io.*;
public class PushbackStreamDemo {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String arg="test2.txt";
try{
PushbackInputStream pushbackInputStream=new PushbackInputStream(
new FileInputStream(arg));
byte[] array=new byte[2];
int tmp=0;
int count=0;
while((count=pushbackInputStream.read(array))!=-1){
//两个字节转换为整数
tmp=(short)((array[0]<<8)|(array[1]&0xff));
tmp=tmp&0xffff;
//判断是否为BIG5,如果是则显示BIG5中文字
if(tmp>=0xA440 && tmp<0xffff)
System.out.println("BIG5:"+new String(array));
else {
//将第二个字节推回流
pushbackInputStream.unread(array,1,1);
//显示ASCII范围的字符
System.out.println("ASCII:"+(char)array[0]);
}

}
pushbackInputStream.close();
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("请指定文件名称");
}
catch(IOException e){
e.printStackTrace();
}

}

}
运行结果:
ASCII:d
BIG5:亲
ASCII:e
BIG5:爱
ASCII:a
BIG5:滴
ASCII:r
BIG5:啊

No comments:

Post a Comment