《第7章 Java的输入输出流.ppt》由会员分享,可在线阅读,更多相关《第7章 Java的输入输出流.ppt(102页珍藏版)》请在课桌文档上搜索。
1、第7章 Java的输入/输出流,7.1 Java语言I/O的类层次 7.2 Java中文件的操作 7.3 特殊的I/O处理流,7.1 Java语言I/O的类层次,Java输入/输出流封装在包java.io中,其常用的层次结构如图7.1、7.2所示。除了图中给出的类外,实际使用中,我们还会碰到File类,它用来描述某个文件的信息。输入/输出类中还有几个比较重要的接口,例如:DataInput、Data Output、File NameFilter。后面我们会作详细的介绍。,图 7.1,图 7.2,Java的流方法从结构上可以分为三大类:字节流、数据流和打印流。字节输入/输出流操作对象为字节(by
2、te);数据流的数据比较多,包含所有基本类型的二进制数据;打印流是机器能够识别的字符形式(包括ASCII码和Unicode码)。,7.2 Java中文件的操作,编写程序免不了要经常与文件打交道,文件的输入/输出需要用到图7.1中所列出的类FileInputStream、FileOutputStream及RandomAccessFile类。前两个类针对于顺序文件的存取,后一个类用于随机文件的读取。后面我们会有详细的介绍。,7.2.1 文件与目录的描述类File File类并不用来进行文件的读/写操作,它用来描述文件对象的属性,既可以表示文件,也可以表示目录。使用它提供的方法,我们可以得到所指对象
3、的描述信息,包括名称、存在否、读/写权限、路径等等。需要注意的是,当我们在Windows环境使用路径时,其分隔符不能是单一的“”符号,因为与C/C+相同,符号“”已经被转意了。例如:,c:jbuilder3javabin路径是非法的,系统不会识别,正确的应该为c:jbilder3javabin下面我们通过表7.1给出File类的一些方法及说明。,表7.1 File类的方法及变量,下面我们给出几个File类的应用实例。通过例题的使用,希望读者对File类有更清楚的认识。例7.1 import java.io.*;public class MyClass1 public static void m
4、ain(String args)Filef=new File(c:jbuilder3myprojectsuntitled5MyClass1.java);if(!f.exists(),Transcript.println(File MyClass1.java doesnt exist!);else Transcript.println(This file can read+f.canRead();Transcript.println(last modified+f.lastModified();Transcript.println(Parent is+f.getParent();Transcri
5、pt.println(File length is+f.length();public MyClass1(),该程序的运行结果如图7.3所示。此程序中,我们为了说明路径分隔符的使用方法,实例化文件对象的时候给出了全路径,其实由于我们已经在Java系统中设置好了源路径,只要给出文件名就行了。表7.1中,list方法用于列出一个目录中所有的文件或与某个模式相匹配的文件。下面我们给出两个例子来讨论带参数或不带参数的list方法的使用。,图 7.3,例7.2import java.io.*;public class MyClass2 public static void main(String arg
6、s)File f1=new File(c:jbuilder3myprojects);if(!f1.isDirectory()Transcript.println(Error:+f1+isnt a directory!);else String dirList=f1.list();for(int i=0;idirList.length;i+),Transcript.println(dirListi);public MyClass2()在该例中,我们并没有在调用File类的list方法中传递参数,这样,处在目录c:jbuilder3myprojects下的所有文件及目录将均被输出,结果如图7.4所
7、示。,图 7.3,图 7.4,程序设计中,往往会碰到根据某一匹配模式来查找目录下的文件的问题,这时就需使用File类带参数的list方法,即 public String list(FilenameFilter filter)其中,参数FilenameFilter是一个接口,只有一个方法的定义:boolean accept(File dir,String name);对于指定目录下的文件,我们可以调用该方法确定某个文件是否包含于其中。比如上例中,我们查找以“un”打头的文件或目录,程序如下:,例7.3import java.io.*;public class MyClass3 implement
8、s FilenameFilter File f;public static void main(String args)File f1=new File(c:jbuilder3myprojects);if(!f1.isDirectory()Transcript.println(Error:+f1+isnt a directory!);else MyClass3 m=new MyClass3(f1);,String dirList=f1.list(m);for(int i=0;idirList.length;i+)Transcript.println(dirListi);public MyCla
9、ss3(File f)this.f=f;public boolean accept(File dir,String name)return name.startsWith(un);,7.2.2 文件I/O处理 1Byte流(字节流)文件的读取 该类的结构如图7.1所示,我们主要用其中的FileOutputStream和FileInputStream类,它们的父类为InputStream和OutputStream。主要的方法有:InputStream int read()int read(byte buf)int read(byte buf,int offset,int length)close
10、(),OutputStreamint write(int c)int write(byte buf)int write(byte buf,int offset,int length)close()下面给出一个应用实例7.4。,图 7.5,例7.4 import java.io.*;public class FileCopy public static void main(String args)throws IOException FileInputStream f1;FileOutputStream f2;f1=new FileInputStream(FileCopy.java);f2=ne
11、w FileOutputStream(acopy_of_java_file);,int temp;while(temp=f1.read()!=-1)f2.write(temp);f1.close();f1.close();public FileCopy()在该例中,我们利用字节流将本程序拷贝至另一个文件acopy_of_java_file中,如果指定的文件不存在,则创建一个新文件,否则原文件的内容会被新写入的内容覆盖。当程序运行后,将生成一个与原程序相同的副本。,2Character流(字符流)文件的读取 该类如图7.2所示,输入/输出类的父类为Reader、Writer,其基本的方法有:Re
12、ader int read()int read(char buf)int read(char buf,int offset,int length)close(),Writer int write(int c)int write(char buf)int write(char buf,int offset,int length)close()读者可与字节流进行比较,注意二者方法的区别。下面我们用字符流来改写例7.4:,例7.5 import java.io.*;public class FileCopy public static void main(String args)throws IOE
13、xception FileReader f1;FileWriter f2;f1=new FileReader(FileCopy.java);f2=new FileWriter(acopy_of_java_file);,int temp;while(temp=f1.read()!=-1)f2.write(temp);f1.close();f2.close();public FileCopy(),例7.6import java.io.*;public class ReadFile public static void main(String args)throws IOException File
14、Reader fr=new FileReader(ReadFile.java);BufferedReader br=new BufferedReader(fr);String line=br.readLine();while(line!=null),Transcript.println(line);line=br.readLine();br.close();本程序中,我们通过类BufferedReader对文件实现按行读取,达到一行一行输出的目的,结果如图7.6所示。,图 7.6,3二进制数据流的文件读取 如果要读取与机器无关的基本数据类型的数据,如整型或浮点型的二进制数,就要用到二进制数据文
15、件流DataInputStream、DataOutputStream。实际使用中,类DataInputStream和DataOutputStream必须和一个输入类(InputStream)或输出类(OutputStream)联接起来,不能直接用文件名或文件对象(File)对其直接初始化,例如:,例7.7import java.io.*;public class DatastreamDemo public static void main(String args)throws IOException FileOutputStream f2=new FileOutputStream(data);
16、DataOutputStream dfo=new DataOutputStream(f2);dfo.writeBoolean(true);,dfo.writeInt(100);dfo.writeFloat(200.2f);f2.close();dfo.close();FileInputStream f1=new FileInputStream(data);DataInputStream dfi=new DataInputStream(f1);boolean b=dfi.readBoolean();int i=dfi.readInt();float f=dfi.readFloat();f1.cl
17、ose();dfi.close();Transcript.println(The value is:);,Transcript.println(+b);Transcript.println(+i);Transcript.println(+f);public DatastreamDemo(),该例中,我们首先利用类DataOutputStream生成一个二进制文件data,并对它写入三个不同类型的数据:布尔型、整型、浮点型。然后利用DataInputStream读入刚刚输入的数据并显示出来,结果如图7.7所示。可以看出,输出结果与我们的输入是一一对应的。,图 7.7,4随机访问文件的读取 对于I
18、nputStream/OutputStream、Reader/Writer类来说,它们都是顺序访问流,只能进行顺序读写。而所谓随机读写,是指读写完上一个字节后,不只能读写其后继的字节,还可以读写文件中任意的字节,就好象文件中有一个随意移动的指针一样。Java语言提供了类RandomAccessFile来进行随机文件的读取。在生成一个RandomAccessFile对象时,不仅要说明文件对象或文件名,同时还需指明访问模式,即“只读方式”(r)或“读写方式”(rw),这类似于C/C+中的fopen()函数。,RandomAccessFile类的功能类似于DataOutputStream类和Data
19、InputStream类的功能合并,即实现了在一个流中进行读、写两种功能。其常用的方法如表7.2所示。,表7.2 RandomAccessFile类的常用方法,例7.8的功能与例7.7一样,只不过是用RandomAccessFile来实现的。例7.8import java.io.*;public class RandomDemo public static void main(String args)throws IOException RandomAccessFilefa=new RandomAccessFile(data,rw);,fa.writeBoolean(true);fa.writ
20、eInt(100);fa.writeFloat(200.2f);fa.seek(0);boolean b=fa.readBoolean();int i=fa.readInt();float f=fa.readFloat();Transcript.println(The value read from a random file is:);Transcript.println(+b);Transcript.println(+i);Transcript.println(+f);,fa.close();public RandomDemo()程序运行结果如图7.8所示。,图 7.8,7.3 特殊的I/
21、O处理流,除了前面我们介绍的一些常用的输入/输出流之外,java.io包中提供了很多特殊的输入/输出流,我们可描述如表7.3所示。,表7.3 java.io包中的特殊输入/输出流,表7.3 java.io包中的特殊输入/输出流,7.3.1 管道流 管道流(PipedStream)可用来把一个程序、线程或代码段的输出直接连接到另一个程序、线程或代码段的输入。Java中管道的类有PipedReader、PipedWriter、PipedInputStream及PipedOutputStream。使用过程中,管道输入流作为一个通信管道的接收端进行数据的读取,管道输出流作为发送端进行数据的传送。下面我
22、们先看一个实例:,例7.9import java.io.*;public class PipedDemo public static void main(String args)throws IOException PipedWriter pw=new PipedWriter();PipedReader pr=new PipedReader(pw);new Read(pw,ReadFile.txt).start();new Write(pr,WriteFile.txt).start();,public PipedDemo()import java.io.*;public class Write
23、 extends Thread PipedReader pr;File f;Write(PipedReader pr,String f)this.pr=pr;this.f=new File(f);public void run()try FileWriter fw=new FileWriter(f);,int data;while(data=pr.read()!=-1)fw.write(data);fw.close();catch(IOException e)public Write()import java.io.*;public class Read extends Thread Pipe
24、dWriter pw;File f;,Read(PipedWriter pw,String f)this.pw=pw;this.f=new File(f);public void run()try FileReader fr=new FileReader(f);int data;while(data=fr.read()!=-1)pw.write(data);pw.close();catch(IOException e),在本例中,我们通过定义两个线程进行管道PipedReader、PipedWriter的连接。线程的概念读者可参考后面章节的内容,这里只要把其理解为可以同时工作的两段程序就可以了
25、。程序运行后,WriteFile.txt的内容就从文件ReadFile.txt中完全拷贝过来。在管道流的初始化过程中,可以给出对应的管道输入/输出流作为参数进行连接,如本例:PipedReader pr=new PipedReader(PipedWriter pw);,或PipedWriter pw=new PipedWriter(PipedReader pr);在Java中,管道输入/输出流还可以通过方法connect()进行连接,如把本例中的程序改为PipedWriter pw=new PipedWriter();PipedReader pr=new PipedReader();pr.co
26、nnect(pw);效果是一样的。读者也可用管道流PipedInputStream、PipedOutputStream来实现上例,这里留给读者自己练习。,7.3.2 内存的I/O流 表7.3给出了Java支持内存读/写的类,总结起来有以下两类。(1)对应字节内存读/写的有ByteArrayInputStream、ByteArrayOutputStream及String BufferInputStream。它们可以从字节数组中读取数据或向字节数组中写入数据。例如:例7.10 import java.io.*;public class MemoryDemo,public static void m
27、ain(String args)throws IOException byte b=111,100,74,98,80,69;byte temp=new byte10;ByteArrayInputStream bi=new ByteArrayInputStream(b);ByteArrayOutputStreambo=new ByteArrayOutputStream();StringBufferInputStream bs=new StringBufferInputStream(A demo for memory input!);int x;while(x=bi.read()!=-1),bo.
28、write(x);Transcript.println(The result of ByteArrayOutputStream is:+bo);bs.read(temp,0,4);Transcript.print(The result of StringBufferOutputStream is:);for(int i=0;i=3;i+)Transcript.print(+tempi);Transcript.println();Transcript.print(+bs);运行结果如图7.9所示。,图 7.9,对于ByteArrayOutputStream来说,我们先从ByteArrayInpu
29、tStream类中把字节流读入bo的缓冲区中,然后直接进行输出,该对象调用自己的toString()进行输出格式转换。(2)对应字符内存读/写的有CharArrayReader、CharArrayWriter、StringReader及StringWriter。它们可以从字符数组中读取数据或向字符数组中写入数据。例如:例7.11 import java.io.*;public class MemoryDemo,public static void main(String args)throws IOException char b=a,t,e,s,t,d,e,m,o;char temp=new
30、 char10;CharArrayReader br=new CharArrayReader(b);CharArrayWriter bw=new CharArrayWriter();StringReader bsr=new StringReader(test demo!);StringWriter bsw=new StringWriter();int x;while(x=br.read()!=-1)bw.write(x);,Transcript.println(The result of CharArrayReader is:+bw);bsr.read(temp,0,4);Transcript
31、.print(The result of StringReader is:);for(int i=0;i=3;i+)Transcript.print(+tempi);Transcript.println();bsw.write(hello,everybody!);Transcript.print(The result of StringWriter is:);Transcript.println(+bsw);程序运行结果如图7.10所示。,图 7.10,我们可以发现以上两个程序的基本架构无大的区别,只是对不同内存流,构造方法有所不同。上述的几种输出内存流,它们在初始化的时候,缺省的缓冲区的大小
32、均为32个字节。当然,实际操作过程中,缓冲区的大小会随数据的写入自动增加。不同的类会有多种不同的方法,方法的使用建立在对各种流的熟悉程度之上,读者应尽可能的学习Java联机文档的内容,获取更多的知识。,7.3.3 多个输入流的连接 如果我们在对文件的操作中需要读取多个文件的话,可使用Java所提供的SequenceInputStream类。该类把多个文件连接起来,形成多个连接的顺序输入流,该流依次打开每个输入流,读取数据,然后关闭该流,并自动切换到下一个输入流。它的两种构造方法如下:,SequenceInputStream(Enumeration e);SequenceInputStream(
33、InputStream s1,InputStream s2);其中,Enumeration类为一接口,处在Java中的util包中,它包含两个方法:public Object nextElement()public boolean hasMoreElements()方法nextElement()取得集合中的下一个对象;方法hasMoreElements()测试集合中的元素是否已经全部取完,并返回一个布尔值。下面是一个利用顺序流对指定的两个文件进行合并操作的例子。,例7.12import java.io.*;import java.util.*;public class ConcaDemo pu
34、blic static void main(String args)throws IOException String s=new String2;s0=PipedDemo.java;s1=MemoryDemo.java;Files flist=new Files(s);,SequenceInputStream si=new SequenceInputStream(flist);FileOutputStreamfo=new FileOutputStream(concaFile.txt);int c;while(c=si.read()!=-1)fo.write(c);si.close();fo.
35、close();import java.io.*;import java.util.*;,public class Files implements Enumeration private String listofFiles;private int current=0;public Files(String listofFiles)this.listofFiles=listofFiles;public boolean hasMoreElements()if(currentlistofFiles.length)return true;else return false;public Objec
36、t nextElement(),try if(!hasMoreElements()return null;else current+;return new FileInputStream(listofFilescurrent-1);catch(Exception e)return null;当我们在JBuilder中运行该程序后,文件concaFile.txt就合并了PipedDemo.java及MemoryDemo.java中的内容。,7.3.4 过滤流 过滤流在读/写数据的同时可以对数据进行处理。Java提供了多个具有过滤功能的输入/输出类,字节类过滤流的父类均为FilterInputSt
37、ream、FilterOutputStream。这两个类为抽象类,包含的子类有:DataInputStream/DataOutputStream BufferedInputStream/BufferedOutputStream LineNumberInputStream PushbackInputStream PrintStream,FilterReader/FilterWriter 过滤流是建立在其他流之上的,也就是说,当使用一个过滤流时,必须首先把过滤流连接到某个输入/输出流上。比如,可以通过输入过滤流的方法read()从下层流中读取数据,通过输出过滤流的write()方法把数据写入下层流
38、。,不同过滤流实现不同的过滤功能,例如:数据的暂存、数据的统计、数据的转换等等。读者对上述的一些类也不必死记,使用的时候,查看相关的联机帮助文档便可获得详细的内容。下面我们分两部分来对过滤流加以介绍:首先介绍如何使用Java系统提供的过滤流类,之后,介绍如何定义自己的过滤流。,1使用过滤流 类BufferedInputStream/BufferedOutputStream实现了带缓冲的过滤流。进行网络编程时,当需要在不同线程之间传送数据时,使用缓冲流会得到较高的性能。数据的输入/输出均是首先读入/写出到缓冲区中,对BufferedOutputStream来说,当数据缓冲区满时,就可以写入所连接
39、的输出流,它所提供的flush()也可强制缓冲区的内容在任何时候全部写入输出流。,过滤流DataInputStream、DataOutputStream实现了接口DataInput、DataOutput。它们可以从所连接的输入流中读取与机器无关的基本数据类型,也可以向所连接的输出流写入基本类型数据。这一点我们可以从例7.7看出。类PrintStream所提供的方法print()、println()可以输出各种类型数据,只是后一种方法在输入结束时自动换行。我们可以利用生成该类的实例进行不同数据的输出。下面我们给出应用过滤流的具体实例:,例7.13import java.io.*;public c
40、lass FilterIODemo public static void main(String args)throws IOException DataInputStream di=new DataInputStream(new BufferedInputStream(System.in);PrintStream po=new PrintStream(new BufferedOutputStream(System.out);String s;,s=di.readLine();while(!s.equals(see you!)po.println(Result:+s);po.flush();s
41、=di.readLine();di.close();po.close();public FilterIODemo(),本例中,System.in、System.out是系统的标准输入/输出类,这里我们指的是控制台,即键盘和屏幕。该段程序运行时,用户可不断输入一些字符,输入的字符会立即显示在屏幕上,直到某一行输入的字符中有“see you!”时,程序结束运行。结果如图7.11所示。,图 7.11,例7.14 import java.io.*;public class PushbackDemo public static void main(String args)throws IOExcepti
42、on PushbackInputStream pis=new PushbackInputStream(System.in);int data;data=pis.read();,while(!Character.isLetter(char)data)data=pis.read();pis.unread(data);while(true)data=pis.read();if(Character.isLetter(char)data)Transcript.print(+(char)data);public PushbackDemo(),程序中语句:while(!Character.isLetter(
43、char)data)data=pis.read();pis.unread(data);的作用是把开头的白字符(white character)过滤掉,并且回吐给输入流过滤掉的第一个字符。,2创建自己的过滤流 程序的编写过程中,往往需要实现自己的过滤流以在读/写操作时可以对数据进行特定的处理,这时就必须自己定义继承于FilterInputStream、FilterOutputStream的过滤流。如果有必要,需重载方法read()、write()或其他一些方法,同时应该确保输入/输出流一起工作。请看下例。,例7.15import java.io.*;public class Main publi
44、c static void main(String args)throws IOException InputStream fin=new FileInputStream(ReadFile.txt);OutputStreamstdout=new FileOutputStream(FileDescriptor.out);,MyInputStream nis=new MyInputStream(fin);MyOutputStream nos=new MyOutputStream(stdout);while(true)nos.write(nis.read();public Main()import
45、java.io.*;public class MyInputStream extends FilterInputStream public int read()throws IOException,int data=in.read();StringBuffer sb=new StringBuffer();while(data!=-1,import java.io.*;public class MyOutputStream extends FilterOutputStream public void write(int b)throws IOException System.out.printl
46、n(Now in OutputStream is:);String data=Integer.toString(b);byte char_array=new bytedata.length();data.getBytes(0,data.length(),char_array,0);out.write(char_array);public MyOutputStream(OutputStream out)super(out);,本例中,定义了两个类:MyInputStream和MyOutputStream,它们均从过滤流FilterInputStream、FilterOutputStream继承而
47、来,同时重写了父类中的read()、write()方法。在类MyInputStream的read()方法中,可以读出文件中所有的数字表达式。因为我们并没有对所有的例外进行捕获,所以,类Main中的语句“while(true)”虽是无限循环,但最终却能由例外NumberFormatException中断。在类MyOutputStream中,由于没有相关的输出字符方法,如DataInputStream中的writeChar()方法,因而我们首先把数字转换为一个字节数组,然后进行输出。假如文件ReadFile.txt的内容为,Number:12 45 89 100 Score:65 89 22 88
48、则最后的输出结果为System Output:Now in OutputStream is:System Output:12System Output:Now in OutputStream is:System Output:45System Output:Now in OutputStream is:,System Output:89System Output:Now in OutputStream is:System Output:100System Output:Now in OutputStream is:System Output:65System Output:Now in Out
49、putStream is:System Output:89System Output:Now in OutputStream is:System Output:22System Output:Now in OutputStream is:System Output:88,7.3.5 解析流 类StreamTokenizer可以通过其对应的语法表把输入流解析为记号流。它通过将输入流中读取的字符与语法表的属性相对照来加以判断空格、数字、字母、串引用符(“”或“”)及注释字符等。类StreamTokenizer中的成员变量ttype用来记录当前所获得的记号类型,对应的关系见表7.4。,表7.4 成员
50、变量ttype属性,表7.4中,sval、nval均为类StreamTokenizer中的成员变量,保存解析出的记号的值。类中提供的方法nextToken()从输入流中读入下一个标记,该方法返回记号的类型,如表7.4示,同时把记号的值保存在相应的变量中。关于其他的用法,读者可查阅相关的文档。下面我们利用类StreamTokenizer来改写例7.15。,例7.16import java.io.*;public class TokenDemo public static void main(String args)throws IOException int sum=0;InputStream