
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 标准输入输出例子
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
//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() ;重命名: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 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,你必须将文件名或文件对象传送给结构: FileInputStream 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 DataInputStreams DataInputStreams与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()成员函数,你可使用\\n,\\r,\\r\\n,或EOF作为字符 结束 符。 读一个长整型,例如: long serialNo;  serialNo = myDataStream.readLong();  9.8 URL 输入流  除了基本文件访问外,Java还 提到了通过网络使用URL访问对象的功能。 在下面这个例子里,我们用getDocumentBase()成员函数并显式指定URL对象来访 问声音和图象。  images[0] = getImage(getDocumentBase(),imageFile();  如果我们愿意,可以直接使用URL:  URL imageSource;  imageSource = new URL("http://555-1212.com/~info");  我们可以为相应的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类 来 完 成 我 们 碰到 的 输 出 流 问 题。 其 它 的 输 出 流 包 含 了 更 多 的 信 息 和 成员 函 数。 象 输 入 流 的 源 文 件 一 样, 这 ?copy; 文 件 在 $JAVA_HOME/src/java/io目录 下。  9.9.1 FileOutputStream类  FileOutputStream对象用于向一个文本文件写数据。象输入文件一样,你得先 打开这个文件后才能写这个文件。 9.9.2 打开一个FileOutputStream对象 要打开一个FileOutputStream对象,象打开一个输入流一样,你可以将字符或文件对象 作为参数: FileOutputStream 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-70,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  public 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('\\n');  }  fos.close();  }  private static void readLine(byte line[]) throws IOException { int i=0,b=0; while ((i }  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还 提 ?copy; 了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 创建随机访问文件 打开随机访问文件有两种方法:  1. 用文件名  myRAFile = new RandomAccessFile(String name,String mode);  2. 用文件对象  myRAFile = new RandomAccessFile(File file,String mode);  mode参数决定了访问文件的权限, 如只读'r'或读写'wr'等。  例如, 我们打开一个数据库更新数据:  RandomAccessFile myRAFile;  myRAFile = new RandomAccessFile("/usr/db/stock.dbf 9.13.2 访问信息 RandomAccessFile对象的读写操作和DataInput/DataOutput对象的操作方式 一 样。 你可以使用在DataInputStream 和DataOutputStream里出现的所有read()和write()函数。  还有几个函数帮助你在文件里移动指针:  long getFilePointer(); 返回当前指针; void seek(long pos); 将文件指针定位到一个绝对地址。地址是相对于文件头的偏移量。地址0表示文件的开头。 long length(); 返回文件的长度。 地址"length()"表示文件的结尾。  9.13.3 增加信息  你可以使用随机访问文件来设置成增加信息模式:  myRAFile = new RandomAccessFile("/tmp/java.log 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;  public class raTest {  public static void main(String args[]) throws IOException {  RandomAccessFile myFAFile;  String s = "Information to Append\\nHi mom!\\n";  //open our random access file  myRAFile = new RandomAccessFile("/tmp/java.log //move to the end of the file  myRAFile.seek(myRAFile.length());  //Start appending!  myRAFile.writeBytes(s);  myRAFile.close();  }  }  本章小结 1. Java通过系统类达到访问标准输入输出的功能。  2. 你可以创建、读、写文件。 
