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?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對象來訪問聲音和圖象。
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(’\n’);
}
fos.close();
}
private?static?void?readLine(byte?line[])
throws?IOException?{
int?i=0,b=0;
while?((i<lineLengh-1))&&((b=System.ini.read())!=’\n’)){
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
myBufferStream.close();
//Always?close?the?"topmost"?file?stream
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()函數。還有幾個函數幫助你在文件里移動指針:
long?getFilePointer();?返回當前指針
void?seek(long?pos);?將文件指針定位到一個絕對地址。地址是相對于文件頭的偏移量。地址0表示文件的開頭。
long?length();?返回文件的長度。地址"length()"表示文件的結尾。
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\nHi?mom!\n";?//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.?你可以創建、讀、寫文件。

來自:http://www.linux8.net/html/java/2005-11/16/21_38_02_170.html