D:/myjava/backup D:/myjava/backup/Write1.txt 6 2002年12月31日05时13分
文件过滤器
类FilterInputStream和FilterOutputStream分别对其他输入/输出流进行特殊处理,它们在读/写数据的同时可以对数据进行特殊处理。另外还提供了同步机制,使得某一时刻只有一个线程可以访问一个输入/输出流
类FilterInputStream和FilterOutputStream分别重写了父类InputStream和OutputStream的所有方法,同时,它们的子类也应该重写它们的方法以满足特定的需要
• 要使用过滤流,首先必须把它连接到某个输入/输出流上,通常在构造方法的参数中指定所要连接的流:
– FilterInputStream(InputStream in);
– FilterOutputStream(OutputStream out);
这两个类是抽象类,构造方法也是保护方法
类BufferedInputStream和BufferedOutputStream实现了带缓冲的过滤流,它提供了缓冲机制,把任意的I/O流“捆绑”到缓冲流上,可以提高读写效率
• 在初始化时,除了要指定所连接的I/O流之外,还可以指定缓冲区的大小。缺省大小的缓冲区适合于通常的情形;最优的缓冲区大小常依赖于主机操作系统、可使用的内存空间以及机器的配置等;一般缓冲区的大小为内存页或磁盘块等地整数倍,如8912字节或更小。
– BufferedInputStream(InputStream in[, int size])
– BufferedOutputStream(OutputStream out[, int size])
例 8.5 列出当前目录中带过滤器的文件名清单。
本例实现FilenameFilter接口中的accept方法,在当前目录中列出带过滤器的文件名。
程序如下:
import java.io.*;
public class DirFilter implements FilenameFilter
{
private String prefix="",suffix=""; //文件名的前缀、后缀
public DirFilter(String filterstr)
{
filterstr = filterstr.toLowerCase();
int i = filterstr.indexOf('*');
int j = filterstr.indexOf('.');
if (i>0)
prefix = filterstr.substring(0,i);
if (j>0)
suffix = filterstr.substring(j+1);
}
public static void main(String args[])
{ //创建带通配符的文件名过滤器对象
FilenameFilter filter = new DirFilter("w*abc.txt");
File f1 = new File("");
File curdir = new File(f1.getAbsolutePath(),""); //当前目录
System.out.println(curdir.getAbsolutePath());
String[] str = curdir.list(filter); //列出带过滤器的文件名清单
for (int i=0;i
System.out.println("/t"+str[i]);
}
public boolean accept(File dir, String filename)
{
boolean yes = true;
try
{
filename = filename.toLowerCase();
yes = (filename.startsWith(prefix)) &
(filename.endsWith(suffix));
}
catch(NullPointerException e)
{
}
return yes;
}
}
程序运行时,列出当前目录中符合过滤条件“w*.txt“的文件名清单。结果如下:
D:/myjava
Write1.txt
Write2.txt
文件对话框
随机文件操作
于InputStream 和OutputStream 来说,它们的实例都是顺序访问流,也就是说,只能对文件进行顺序地读/写。随机访问文件则允许对文件内容进行随机读/写。在java中,类RandomAccessFile 提供了随机访问文件的方法。类RandomAccessFile的声明为:
public class RandomAccessFile extends Object implements DataInput, DataOutput
File:以文件路径名的形式代表一个文件
FileDescriptor:代表一个打开文件的文件描述
FileFilter & FilenameFilter:用于列出满足条件的文件
File.list(FilenameFilter fnf)
File.listFiles(FileFilter ff)
FileDialog.setFilenameFilter(FilenameFilter fnf)
• FileInputStream & FileReader:顺序读文件
• FileOutputStream & FileWriter:顺序写文件
• RandomAccessFile:提供对文件的随机访问支持
类RandomAccessFile则允许对文件内容同时完成读和写操作,它直接继承Object,并且同时实现了接口DataInput和DataOutput,提供了支持随机文件操作的方法
DataInput和DataOutput中的方法
• readInt(), writeDouble()…
int skipBytes(int n):将指针乡下移动若干字节
length():返回文件长度
long getFilePointer():返回指针当前位置
void seek(long pos):将指针调到所需位置
void setLength(long newLength):设定文件长度
构造方法:
RandomAccessFile(File file, String mode)
RandomAccessFile(String name, String mode)
mode 的取值
– “r” 只读. 任何写操作都将抛出IOException。
– “rw” 读写. 文件不存在时会创建该文件,文件存在时,原文件内容不变,通过写操作改变文件内容。
– “rws” 同步读写. 等同于读写,但是任何协操作的内容都被直接写入物理文件,包括文件内容和文件属性。
– “rwd” 数据同步读写. 等同于读写,但任何内容写操作都直接写到物理文件,对文件属性内容的修改不是这样。
例 8.6 随机文件操作。
本例对一个二进制整数文件实现访问操作当以可读写方式“rw“打开一个文件”prinmes.bin“时,如果文件不存在,将创建一个新文件。先将2作为最小素数写入文件,再依次测试100以内的奇数,将每次产生一个素数写入文件尾。
程序如下:
import java.io.*;
public class PrimesFile
{
RandomAccessFile raf;
public static void main(String args[]) throws IOException
{
(new PrimesFile()). createprime(100);
}
public void createprime(int max) throws IOException
{
raf=new RandomAccessFile("primes.bin","rw");//创建文件对象
raf.seek(0); //文件指针为0
raf.writeInt(2); //写入整型
int k=3;
while (k<=max)
{
if (isPrime(k))
raf.writeInt(k);
k = k+2;
}
output(max);
raf.close(); //关闭文件
}
public boolean isPrime(int k) throws IOException
{
int i=0,j;
boolean yes = true;
try
{
raf.seek(0);
int count = (int)(raf.length()/4); //返回文件字节长度
while ((i<=count) && yes)
{
if (k % raf.readInt()==0) //读取整型
yes = false;
else
i++;
raf.seek(i*4); //移动文件指针
}
} catch(EOFException e) { } //捕获到达文件尾异常
return yes;
}
public void output(int max) throws IOException
{
try
{
raf.seek(0);
System.out.println("[2.."+max+"]中有 "+
(raf.length()/4)+" 个素数:");
for (int i=0;i
{
raf.seek(i*4);
System.out.print(raf.readInt()+" ");
if ((i+1)%10==0) System.out.println();
}
} catch(EOFException e) { }
System.out.println();
}
}
程序运行时创建文件“primes.bin“,并将素数写入其中,结果如下:
[2..100]中有 25 个素数:
2 3 5 7 11 13 17 19 23 29
31 37 41 43 47 53 59 61 67 71
73 79 83 89 97
第四节 字符流 Reader类和Writer类 前面说过,在JDK1.1之前,java.io包中的流只有普通的字节流(以byte为基本处理单位的流),这种流对于以16位的Unicode码表示的字符流处理很不方便。从JDK1.1开始, java.io包中加入了专门用于字符流处理的类,它们是以Reader和Writer为基础派生的一系列类 同类InputStream和OutputStream一样,Reader和Writer也是抽象类,只提供了一系列用于字符流处理的接口。它们的方法与类InputStream和OutputStream类似,只不过其中的参数换成字符或字符数组 Reader类 • void close() • void mark(int readAheadLimit) • boolean markSupported() : • int read() • int read(char[] cbuf) • int read(char[] cbuf, int off, int len) • boolean ready() • void reset() • long skip(long n) Writer类 • void close() • void flush() • void write(char[] cbuf) • void write(char[] cbuf, int off, int len) • void write(int c) • void write(String str) • void write(String str, int off, int len) 例 8.7 文件编辑器。 本例实现文件编辑器中的打开、保存文件功能。程序如下: import java.awt.*; import java.awt.event.*; import java.io.*; public class EditFile1 extends WindowAdapter implements ActionListener,TextListener { Frame f; TextArea ta1; Panel p1; TextField tf1; Button b1,b2,b3; FileDialog fd; File file1 = null; public static void main(String args[]) { (new EditFile1()).display(); } public void display() { f = new Frame("EditFile"); f.setSize(680,400); f.setLocation(200,140); f.setBackground(Color.lightGray); f.addWindowListener(this); tf1 = new TextField(); tf1.setEnabled(false); tf1.setFont(new Font("Dialog",0,20)); //设置文本行的初始字体 f.add(tf1,"North"); ta1 = new TextArea(); ta1.setFont(new Font("Dialog",0,20)); //设置文本区的初始字体 f.add(ta1); ta1.addTextListener(this); //注册文本区的事件监听程序 p1 = new Panel(); p1.setLayout(new FlowLayout(FlowLayout.LEFT)); b1 = new Button("Open"); b2 = new Button("Save"); b3 = new Button("Save As"); p1.add(b1); p1.add(b2); p1.add(b3); b2.setEnabled(false); b3.setEnabled(false); b1.addActionListener(this); //注册按钮的事件监听程序 b2.addActionListener(this); b3.addActionListener(this); f.add(p1,"South"); f.setVisible(true); } public void textValueChanged(TextEvent e) { //实现TextListener接口中的方法,对文本区操作时触发 b2.setEnabled(true); b3.setEnabled(true); } public void actionPerformed(ActionEvent e) { if (e.getSource()==b1) //单击[打开]按钮时 { fd = new FileDialog(f,"Open",FileDialog.LOAD); fd.setVisible(true); //创建并显示打开文件对话框 if ((fd.getDirectory()!=null) && (fd.getFile()!=null)) { tf1.setText(fd.getDirectory()+fd.getFile()); try //以缓冲区方式读取文件内容 { file1 = new File(fd.getDirectory(),fd.getFile()); FileReader fr = new FileReader(file1); BufferedReader br = new BufferedReader(fr); String aline; while ((aline=br.readLine()) != null)//按行读取文本 ta1.append(aline+"/r/n"); fr.close(); br.close(); } catch (IOException ioe) { System.out.println(ioe); } } } if ((e.getSource()==b2) || (e.getSource()==b3)) { //单击[保存]按钮时 if ((e.getSource()==b3) ||(e.getSource()==b2)&&(file1==null)) { //单击[SaveAs]按钮时,或单击[Save]按钮且文件对象为空时 fd = new FileDialog(f,"Save",FileDialog.SAVE); if (file1==null) fd.setFile("Edit1.txt"); else fd.setFile(file1.getName()); fd.setVisible(true); //创建并显示保存文件对话框 if ((fd.getDirectory()!=null) && (fd.getFile()!=null)) { tf1.setText(fd.getDirectory()+fd.getFile()); file1 = new File(fd.getDirectory(),fd.getFile()); save(file1); } } else save(file1); } } public void save(File file1) { try //将文本区内容写入字符输出流 { FileWriter fw = new FileWriter(file1); fw.write(ta1.getText()); fw.close(); b2.setEnabled(false); b3.setEnabled(false); } catch (IOException ioe) { System.out.println(ioe); } } public void windowClosing(WindowEvent e) { System.exit(0); } } 第五节 字节流的高级应用 管道流 管道用来把一个程序、线程和代码块的输出连接到另一个程序、线程和代码块的输入。java.io中提供了类PipedInputStream和PipedOutputStream作为管道的输入/输出流 管道输入流作为一个通信管道的接收端,管道输出流则作为发送端。管道流必须是输入输出并用,即在使用管道前,两者必须进行连接 管道输入/输出流可以用两种方式进行连接: – 在构造方法中进行连接 • PipedInputStream(PipedOutputStream pos); • PipedOutputStream(PipedInputStream pis); – 通过各自的connect()方法连接 • 在类PipedInputStream中,connect(PipedOutputStream pos); • 在类PipedOutputStream中,connect(PipedInputStream pis); 例 8.8 管道流。 本例例管道流的使用方法。设输入管道in与输出管道out已连接,Send线程向输出管道out发送数据,Receive线程从输入管道in中接收数据。程序如下: import java.io.*; public class Pipedstream { public static void main (String args[]) { PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(); try { in.connect(out); } catch(IOException ioe) { } Send s1 = new Send(out,1); Send s2 = new Send(out,2); Receive r1 = new Receive(in); Receive r2 = new Receive(in); s1.start(); s2.start(); r1.start(); r2.start(); } } class Send extends Thread //发送线程 { PipedOutputStream out; static int count=0; //记录线程个数 int k=0; public Send(PipedOutputStream out,int k) { this.out= out; this.k= k; this.count++; //线程个数加1 } public void run( ) { System.out.print("/r/nSend"+this.k+": "+this.getName()+" "); int i=k; try { while (i<10) { out.write(i); i+=2; sleep(1); } if (Send.count==1) //只剩一个线程时 { out.close(); //关闭输入管道流 System.out.println(" out closed!"); } else this.count--; //线程个数减1 } catch(InterruptedException e) { } catch(IOException e) { } } } class Receive extends Thread //接收线程 { PipedInputStream in; public Receive(PipedInputStream in) { this.in = in; } public void run( ) { System.out.print("/r/nReceive: "+this.getName()+" "); try { int i = in.read(); while (i!=-1) //输入流未结束时 { System.out.print(i+" "); i = in.read(); sleep(1); } in.close(); //关闭输入管道流 } catch(InterruptedException e) { } catch(IOException e) { System.out.println(e); } } } 程序运行结果如下: Send1: Thread-0 Send2: Thread-1 Receive: Thread-2 1 Receive: Thread-3 2 3 4 5 7 out closed! 6 8 9 java.io.IOException: Pipe closed! 数据流 DataInputStream和DataOutputStream 在提供了字节流的读写手段的同时, 以统一的通用的形式向输入流中写入boolean,int,long,double等基本数据类型,并可以在次把基本数据类型的值读取回来。 提供了字符串读写的手段。 分别实现了DataInput和DataOutput接口 声明类: Public class DataInputStream extends filterInputStream implements DataInput 例 8.9 数据流。 本例演示数据流的使用方法。 程序如下: import java.io.*; public class Datastream { public static void main(String arg[]) { String fname = "student1.dat"; new Student1("Wang").save(fname); new Student1("Li").save(fname); Student1.display(fname); } } class Student1 { static int count=0; int number=1; String name; Student1(String n1) { this.count++; //编号自动加1 this.number = this.count; this.name = n1; } Student1() { this(""); } void save(String fname) { try { //添加方式创建文件输出流 FileOutputStream fout = new FileOutputStream(fname,true); DataOutputStream dout = new DataOutputStream(fout); dout.writeInt(this.number); dout.writeChars(this.name+"/n"); dout.close(); } catch (IOException ioe){} } static void display(String fname) { try { FileInputStream fin = new FileInputStream(fname); DataInputStream din = new DataInputStream(fin); int i = din.readInt(); while (i!=-1) //输入流未结束时 { System.out.print(i+" "); char ch ; while ((ch=din.readChar())!='/n') //字符串未结束时 System.out.print(ch); System.out.println(); i = din.readInt(); } din.close(); } catch (IOException ioe){} } } 程序运行结果如下: 1 Wang 2 Li 对象流 • 对象的持续性(Persistence) – 能够纪录自己的状态一边将来再生的能力,叫对象的持续性 • 对象的串行化(Serialization) – 对象通过写出描述自己状态的的数值来记录自己的过程叫串行化。串行化的主要任务是写出对象实例变量的数值,如果变量是另一个对象的引用,则引用的对象也要串行化。这个过程是递归的 • 对象流 – 能够输入输出对象的流称为对象流。 – 可以将对象串行化后通过对象输入输出流写入文件或传送到其它地方 在java中,允许可串行化的对象在通过对象流进行传输。只有实现Serializable接口的类才能被串行化, Serializable接口中没有任何方法,当一个类声明实现Serializable接口时,只是表明该类加入对象串行化协议 要串行化一个对象,必须与一定的对象输出/输入流联系起来,通过对象输出流将对象状态保存下来(将对象保存到文件中,或者通过网络传送到其他地方) ,再通过对象输入流将对象状态恢复 类ObjectOutputStream和ObjectInputStream分别继承了接口ObjectOutput和ObjectInput,将数据流功能扩展到可以读写对象,前者用writeObject()方法可以直接将对象保存到输出流中,而后者用readObject()方法可以直接从输入流中读取一个对象 例 8.10 对象流。 本例声明Student2为序列化的类。Save方法中,创建对象输出流out,并以添加方式向文件中直接写入当前对象out.writeObject(this);display方法中,创建对象输入流in,从文件中直接读取一个对象in.readObject(),获得该对象的类名、接口名等属性,并显示其中的成员变量。程序如下: import java.io.*; public class Student2 implements Serializable //序列化 { int number=1; String name; Student2(int number,String n1) { this.number = number; this.name = n1; } Student2() { this(0,""); } void save(String fname) { try { FileOutputStream fout = new FileOutputStream(fname); ObjectOutputStream out = new ObjectOutputStream(fout); out.writeObject(this); //写入对象 out.close(); } catch (FileNotFoundException fe){} catch (IOException ioe){} } void display(String fname) { try { FileInputStream fin = new FileInputStream(fname); ObjectInputStream in = new ObjectInputStream(fin); Student2 u1 = (Student2)in.readObject(); //读取对象 System.out.println(u1.getClass().getName()+" "+ u1.getClass().getInterfaces()[0]); System.out.println(" "+u1.number+" "+u1.name); in.close(); } catch (FileNotFoundException fe){} catch (IOException ioe){} catch (ClassNotFoundException ioe) {} } public static void main(String arg[]) { String fname = "student2.obj"; Student2 s1 = new Student2(1,"Wang"); s1.save(fname); s1.display(fname); } } 程序运行结果如下: Student2 interface java.io.Serializable 1 Wang