|
Java 天地
9.1 Java 输入输出流所有的程序语言都提供与本机文件系统交互的方式;Java也不例外。 我们将看看Java是怎样处理标准文件输入输出的(包 括stdin,stout,stderr)。 当你在网络上开发小程序时, 你必须注意直接文件输入输出是不安全因素的关键。 大多数用户设置他们的浏览器, 可让你自由的访问他们的文件系统, 但有时不让你访问。 当然, 如果你开发你内部的应用程序, 你也许需要直接访问文件。 标准输入输出: Unix的用户, 或其他基于命令行系统的用户(如DOS), 都知道标准输入输出的含义。 标准输入文件是键盘, 标准输出文件是你的终端屏幕。 标准错误输出文件也指向屏幕, 如果有必要, 它也可以指向另一个文件以便和正常输出区分。 系统类: Java通过系统类达到访问标准输入输出的功能。上面提到的三个文件在这个系统类中实现: Stdin: System.in作为InputStream类的一个实例来实现stdin, 你可以使用read()和skip(long n)两个成员函数。read()让你从输入中读一个字节, skip(long n)让你在输入中跳过n个字节。 Stout: System.out作 为PrintStream来实现stdout, 你可以使用print()和println()两个成员函数。 这两个函数支持Java的任意基本类型作为参数。 Stderr: System.err同stdout一样实现stderr。 象System.out一样, 你可以访问PrintStream 成员函数。 9.2 标准输入输出例子这里有一个例子, 功能象Unix里的cat或type: import java.io.* class myCat{ public void main(String args[]) throws IOException{ int b; int count = 0; while ((b = System.in.read()) != -1){ count++; System.out.print((char)b); } System.out.println(); //blank line System.err.println("counted"+count+"total bytes."); } } 9.3 普通输入输出类除了基本的键盘输入和屏幕输出外, 我们还需要联系文件的输入输出。 我们将学习下面几个类: | FileInputStream | DataInputStream | FileOutputStream | DataOutputStream 作为参考, 再列出一些特定应用的类: | PipedInputStream | BufferedInputStream | PushBackInputStream | StreamTokenizer | PipedOutputStream | BufferedOutputStream | RandomAccessFile 我们不在此讨论这些类, 但你可以在JAVA_HOME/src/java/io目录里查看每个类的成员函数定义。 9.4 文件在我们进行文件操作时, 需要知道一些关于文件的信息。File类提供了一些成员函数来操纵文件和获得一些文件的信息。 9.4.1 创建一个新的文件对象你可用下面三个方法来创建一个新文件对象: File myFile; myFile = new File("etc/motd"); 或 myFile = new File("/etc","motd"); //more useful if the directory or filename are variables 或 File myDir = new file("/etc"); myFile = new File(myDir,"motd"); 这三种方法取决于你访问文件的方式。 例如, 如果你在应用程序里只用一个文件, 第一种创建文件的结构是最容易的。 但如果你在同一目录里打开数个文件, 则第二种或第三种结构更好一些。 9.4.2 文件测试和使用一旦你创建了一个文件对象, 你便可以使用以下成员函数来获得文件相关信息: 文件名: | String getName() | String getPath() | String getAbslutePath() | String getParent() | boolean renameTo(File newName) 文件测试: | boolean exists() | boolean canWrite() | boolean canRead() | boolean isFile() | boolean isDirectory() | boolean isAbsolute() 一般文件信息: | long lastModified() | long length() 目录用法: | boolean mkdir() | String[] list() 9.4.3 文件信息获取例子程序这里是一个独立的显示文件的基本信息的程序, 文件通过命令行参数传输: import java.io.*; class fileInfo{ File fileToCheck; public static void main(String args[]) throws IOException{ if (args.length>0){ for (int i=0;i<args.length;i++){ fileToCheck = new File(args[i]); info(fileToCheck); } } else{ System.out.println("No file given."); } } public void info (File f) throws IOException { System.out.println("Name: "+f.getName()); System.out.println("Path: "=f.getPath()); if (f.exists()) { System.out.println("File exists."); System.out.print((f.canRead() ?" and is Readable":"")); System.out.print((f.cnaWrite()?" and is Writeable":"")); System.out.println("."); System.out.println("File is " + f.lenght() = " bytes."); } else { System.out.println("File does not exist."); } } } 9.5 输入流InputStream SequenceInputStream FileInputStream PipedInputStream ByteArrayInputStream FileterInputStream StringBufferInputStream DataInputStream LineNumberInputStream PushbackInputStream BufferedInputStream 有好几个类是专门用来处理文件输入的。 下面是文件输入类的层次结构: 9.5.1 FileInputStream 对象FileInputStream典型地表示一种顺序访问的文本文件。 通过使用FileInputStream你可以访问文件的一个字节、 几个字节或整个文件。 9.5.2 打开FileInputStream为一个文件打开输入流FileInputStream, 你必须将文件名或文件对象传送给结构: FileInput Stream myFileStream; myFileStream = new FileInputStream ( "/etc/motd"); 你还可以象下边这样从FileInputStream里读文件信息: File myFile; FileInputSteam myFileStream; myFile = new File("/etc/motd"); myFileStream = new FileInputStream(myFile); 一旦FileInputStream输入流打开, 你就可以从里面读取信息了。read()成员函数有以下几种选项: int read() //reads one byte //return -1 at end of stream int read(byte b[]) //fills entire array,if possible //returns number of bytes read //returns -1 if end of stream is reached int read(byte b[],int offset, int len) //reads len bytes into b starting at b[offset] //Returns number of bytes read, //or -1 if end of stream is reached. 9.5.3 关闭FileInputStream当你完成一个文件的操作, 你可选两种方法关闭它: 显式关闭和隐式关闭, 隐式关闭是自动垃圾回收时的功能。 显式关闭如下: myFileStream.close(); 9.6 例程: 显示一个文件如果文件的访问权限足够, 你可以在TextArea对象里显示文件内容。 下面是显示文件的程序片断: FileInputStream fis; TextArea ta; public vod init(){ byte b[] = new byte [1024]; int I; //make it big enough or wait until you //know the size of the file String s; try { fis = new FileInputStream("/etc/motd"); } catch(FileNotFoundException e) { /*do something appropriate */ } try { I= fis.read(b); } catch(IOException e) { /* do something appropriate */ } s = new String(b, 0); ta = new TextArea(s,5,40); add (ta); } 9.7 DataInputStreamsDataInputStreams与FileInputStreams差不多。Data流可以直接读任意一种变量类型, 如浮点 、整数和字符等。 一般来说, 对二进制文件使用DataInputStream流。 9.7.1 打开和关闭DataInputStreams打开和关闭DataInputStreams对象时, 其方法与FileInputStreams相同: DataInputStreams myDataStream; FileInputStreams myFileStream; //get a file handle myFileStream = new FileInputStream("/usr/db/stock.dbf"); //open,or "chain" a data input file myDataStream = new DataOutputStream(myFileStream); //Now we can use both input streams to access our file //j(If we want to...) myFileStream.read(b); I = myDataStrea.readInt(); //close the data friel explicityly //Always close the "topmost" file stream myDataStream.close(); myFileStream.close(); 9.7.2 读DataInputStreams当你从DataInputStreams流里访问文件时, 你可以使用与FileInputStream流相同的成员函数read()。 但你也可以使用其他访问方法来读取不同种类的数据: | byte readByte() | int readUnsignedByte() | short readShort() | int readUnsighedShort() | char readChar() | int readInt | long readLong() | float readFloat() | double readDouble() | String readLine() 以上每一个成员函数都读取相应的数据对象。 象String readLine()成员函数, 你可使用 读 一 个 长 整 型, 例 如: long serialNo; ... serialNo = myDataStream.readLong(); 9.8 URL 输入流除了基本文件访问外, Java还提供了通过网络使用URL访问对象的功能。 在下面这个例子里, 我们用getDocumentBase()成员函数并显式指定URL对象来访问声音和图象。 String imageFile = new String ("images/Duke/T1.gif"); images[0] = getImage(getDocumentBase(),imageFile(); 如果我们愿意, 可以直接使用URL: URL imageSource; imageSource = new URL("http://555-1212.com/~info"); images[0] = getImage(imageSource,"Duke/T1.gif"); 我们可以为相应的URL打开输入流。 例如, 下面的程序里包括一个数据文件: InputStream is; byte buffer[] = new byte[24]; is = new URL(getDocumentBase(),dataname).openStream(); 现在我们可以使用is, 就象使用FileInputStream对象一样: is.read(buffer.0,buffer.length); 注意: 有些用户设置了他们的浏览器安全属性, 可以不让你的程序访问他们的文件。 9.9 OutputStreams上面我们谈到了读数据, 那么如何实现写数据呢? 象输入流一样, 输出流也有类似的层次结构: OutputStream FileOutputStream | PipedOutputStream | ByteArrayOutputStream FilterOutputStream DataOutputStream | PrintStream | BufferedOutputStream 我们将分析FileOutputStream和DataOutputStream类来完成我们碰到的输出流问题。 其它的输出流包含了更多的信息和成员函数。 象输入流的源文件一样, 这些文件在 $JAVA_HOME/src/java/io目录下。 9.9.1 FileOutputStream类FileOutputStream对象用于向一个文本文件写数据。 象输入文件一样, 你得先打开这个文件后才能写这个文件。 9.9.2 打开一个FileOutputStream对象要打开一个FileOutputStream对象, 象打开一个输入流一样, 你可以将字符串或文件对象作为参数: FileOutputStream myFileStream; myFileStream = new FileOutputStream("/etc/motd"); 象输入流一样, 你也可这样使用: File myFile; FileOutputStream myFileStream; myFile = new File("/etc/motd"); myFileStream = new FileOutputStream(myFile); 9.9.3 写入一个流一旦文件被打开, 你便可以使用write()函数向文件里写一些数据。 就象输入流的read()函数一样, 你可有三种方法: void write(int b); //writes out one byte void write(byte b[]); //writes out entire array void write (byte b[],int offset,int length); //write out length bytes of b[],starting at b[offset] 9.9.4 关闭一个FileOutputStream对象关闭输出流和关闭输入流方法一样, 你可以使用显式方法: myFileStream.close(); 你也可以让系统自动关闭它。 9.10 例子: 存储信息下面有一个程序, 让用户输入一些姓名和电话号码。每一个姓名和号码将加在文件里。 用户通过点“Done"按钮来告诉系统整个列表已输入完毕。 一旦用户输入完整个列表, 程序将创建一个输出文件并显示或打印出来。 例如: 555-1212,Tom 123-456-7890,Peggy L. 234-5678,Marc 234-5678,Ron 876-4321,Beth&Brian 33.1.42.45.70,Jean-Marc 下面是程序的源代码: import java.io.*; //Phones.java //A simple database creation program class Phones { static FileOutputStream fos; public static final int lineLength = 81; public static void main(String args[]) throws IOExciption { byte[] phone = new byte[lineLength]; byte[] name = new byte[lineLenght]; int I; fos = new FileOutputStream("phone.numbers"); while (true) { System.err.println("Enter a name (enter `done` to quit)"); readLine(name); if ("done".equalsIgnoreCase(new String(name,0,0,4))) { break; } System.err.println("Enter the phone number"); readLine(phone); for ( i=0;phone[i]!= 0;i++) { fos.write(phone[i]); } fos.write(`,`); for (i=0;name[i]!= 0;I++) { fos.write(name[i]); } fos.write(``); } fos.close(); } private static void readLine(byte line[]) throws IOException { int i=0,b=0; while ((i<lineLengh-1))&&((b=System.ini.read())!=``)) { line[i++] = (byte)b; } line[i]=(byte) 0; } } 9.11 BufferedOutput流如果你处理的数据量很多, 或向文件写很多次小数据, 你可以使用一个BufferedOutput流。 BufferedOutput流提供和FileOutputStream类同样的写操作方法, 但所有输出全部存放在一个缓冲区里。 当你填满缓冲区, 它将一次性写入磁盘。 或者你主动将缓冲区写入磁盘。 9.11.1 创建BufferedOutput流如果要创建一个BufferedOutput流, 首先需要一个FileOutput流。然后将缓冲区链接到FileOutput流: FileOutputStream myFileStream; BufferedOutputStream myBufferStream; //get a file handle myFileStream = new FileOutputStream("/usr/db/stock.dbf"); //chain a buffered output stream myBufferSSstream = new BufferedOutputStream(myFileStream); 9.11.2 更新和关闭BufferedOutput流和普通FileOutput流一样, 向BufferedOutput流里的每一次写操作和写入磁盘操作并不是一一对应的。 要想在程序结束之前将缓冲区里的数据写入磁盘, 除非填满缓冲区, 否则只有显式调用flush()函数: //force left-over data to disk myBufferStream.flush(); //close the data file explicitly //Always close the "topmost" file stream myBufferStream.close(); myFileStream.close(); 9.12 DataOutput流和DataInputStream对应, Java还提供了DataOutput流。 使用DataOutput流, 我们可以向文件写入二进制数据。 9.12.1 打开和关闭DataOutput流对象打开和关闭DataOutput流对象与打开、 关闭FileOutput流对象方法一样: DataOutputStream myDataStream; FileOutputStream myFileStream; BufferedOutputStream myBufferStream; //get a file handle mhyFileStream = new FileOutputStream("/usr/db/stock.dbf"); //chain a buffered output stream (for efficiency); myBufferStream = new BufferedOutputStream(myFileStream); //chain a data output file myDataStream = new DataOutputStream(myBufferStream); //Now we can use both input streams to access our file //(iiIf we want to ...) myBufferStream.write(b); myDataStream.writeInt(i); //close the data file explicitly //Always colse the "topmost" file stream myDataStream.close(); myBuffersStream.close(); myFileStream.close(); 9.12.2 向DataOutput流写数据FileOutput流里的write()函数各种方法都适用于DataOutput流。你还可以看到DataInput流的类似函数方法: | void writeBoolean (boolean v) | void writeByte (int v) | void writeShort (int v) | void writeChar (int v) | void writeInt (int v) | void writeFloat (float v) | void writeDouble (double v) | void writeBytes (string s) | void writeChars (string s) 对字符串来说, 有两种选择: byte和char。 记住byte是8位数据而char是16位数据。 如果你想利用Unicode字符的优点, 你应使用writeChars()函数。 9.12.3 输出记数在使用二进制数据输出时常用的另外一个函数是size()。 这个函数返回写入文件数据的总字节数。 你也可用size()函数将数据文件分成四字节为单位的块, 例如: ... int bytesLeft = myDataStream.size()%4; for (int I = 0; I< bytesLeft; I++) { myDataStrea.write(0); } ... 9.13 随机访问文件我们读文件常常不是从头至尾顺序读的。 你也许想将一文本文件当作一个数据库, 读完一个记录后, 跳到另一个记录, 它们在文件的不同地方。Java提供了RandomAccessFile类让你操作这种类型的输入输出。 9.13.1 创建随机访问文件打开随机访问文件有两种方法: 用文件名 myRAFile = new RandomAccessFile(String name,String mode); 用文件对象 myRAFile = new RandomAccessFile(File file,String mode); mode参数决定了访问文件的权限, 如只读`r`或读写`wr`等。 例如, 我们打开一个数据库更新数据: RandomAccessFile myRAFile; myRAFile = new RandomAccessFile("/usr/db/stock.dbf","rw"); 9.13.2 访问信息RandomAccessFile对象的读写操作和DataInput/DataOutput对象的操作方式一样。 你可以使用在DataInputStream 和DataOutputStream里出现的所有read()和write()函数。 还有几个函数帮助你在文件里移动指针:
9.13.3 增加信息你可以使用随机访问文件来设置成增加信息模式: myRAFile = new RandomAccessFile("/tmp/java.log","rw"); myRAFile.seek(myRAFile.length()); //Any subsequent write()s will be appended to the file 9.13.4 追加信息例子: 下面是一个在已存在文件后面追加字符串的例子: import java.io.IOException; import java.io.RandomAccessFile; class raTest { public static void main(String args[]) throws IOException { RandomAccessFile myFAFile; String s = "Information to Append Hi mom!"; //open our random access file myRAFile = new RandomAccessFile("/tmp/java.log","rw"); //move to the end of the file myRAFile.seek(myRAFile.length()); //Start appending! myRAFile.writeBytes(s); myRAFile.close(); } } 本章小结1. Java通过系统类达到访问标准输入输出的功能。 2. 你可以创建、读、写文件。 (作者: 袁小春 来源: 不详) |