2013年9月10日 星期二

File input and output



作者: 許裕永

輸入:把資料讀入程式中。輸出:把資料從程式中送出。目前為止,我們都從鍵盤輸入資料;把資料輸出至螢幕。但實務上更多時候是把某一個檔案的資料讀入,或把資料輸出至某一個檔案之中。本章著重的,便是檔案的管理及資料的傳送。
從本章開始,後續所使用的類別中的部份方法在呼叫時,編譯時期會丟出Exception,而無法編譯。
例:JavaTest.java:4: unreported exception java.io.FileNotFoundException;
關於Exception的處理,本書將於後續章節中介紹,此時讀者只需要將該Exception的名稱,宣告於main()方法後的throws
的後面就可以了。
例:main(String〔〕 args)throws FileNotFoundException{………}
若編譯時還有其他Exception,一樣將該Exception的名稱,以‘,’隔開,串聯於throws後方即可。
例:main(String[] args)throws FileNotFoundException,IOException{…..}

1. File
java‧io‧File類別建構的物件,可以協助設計師管理檔案或資料夾,但是請不要被這個類別的名字給混淆了。用File建構的物件是一個Java的物件,是儲存在特定記憶體區塊的資料,不是一個實質的作業系統中的檔案。它只是一個可以代表某一個檔案或資料夾的物件。
1-1 建構物件
建構方法一:File(String pathname)
將檔案或資料夾的路徑以字串值為參數來建構File物件。注意事項有二:1、路徑分隔符號是‘/’,不是‘\’。2、若不是以磁碟機代號起頭,則預設為執行檔的所在位置。
例一:
File f=new File(“c:/java/test.txt”);
File f2=new File(“test.txt”);
建構方法二:File(String parent, String child)
第一個參數為父路徑,第二個參數為子路徑,兩個參數串起來即為完整路徑。
建構方法三:File(File parent, String child)
用一個File物件做父路徑,再用字串物件做子路徑。
 
例一:
File f=new File(“c:/java”);
File f2=new File(f,”test.txt”);
看完上述三個建構方法,不知道各位讀者有沒有看出來,給什麼樣式的參數所建構的物件代表檔案?什麼樣式的參數所建構的物件代表資料夾?因為檔案名稱後方都 會用‘‧’加副檔名,所以大部份的人都會誤以為字串參數中有加“‧”,建構的物件自然代表檔案,若沒有‘‧’的字串參數建構的物件則代表資料夾。這種看法 是錯的。
File類別建構的物件,可以代表實體磁碟中的檔案或資料夾。而不是File可以建構成檔案物件或資料夾物件。它是代表參數所指定的那個實體磁碟中的一個東西,不管那個東西是資料夾或檔案。
例:File f=new File(“c:/abc‧bb”);
如果C磁碟中有一個名為“abc‧bb”的資料夾,那麼f就代表那個資料夾。反之,如果C磁碟中有一個名為“abc‧bb”的檔案,那麼f就代表那個檔案。
但如果C磁碟中既沒有名為“abc‧bb”的資料夾,也沒有名為“abc‧bb”的檔案,也就是指定的路徑不存在時,f便不代表任何檔案或資枓夾。而是等著設計師利用這個物件呼叫適當的方法,來建立實體磁碟中的新資料夾或新檔案。
 
1-2 資料夾管理
方法一:boolean exists()
運算結果:物件代表的資料夾或檔案是否存在。
方法二:boolean mkdir()
運算結果:依建構物件的路徑參數,在實體磁碟中建立一個新的資料夾,若建立成功則運算結果為true。若實體磁碟中已存在相同名稱的資料夾,將不會建立新資料夾,而且運算結果為false。
方法三:boolean mkdirs()
運算結果:若建構物件的路徑參數中,含有一個以上的新資料夾名稱,則必須呼叫此方法才可以將所有新資料夾全部建立,若建立成功則運算結果為true,反之運算結果為false。
例一:
File f=new File("c:/abc.bb");
if(!f.exists()){
 f.mkdir();
 System.out.println("資料夾建立成功");
}else{
 System.out.println("資料夾已經存在");
}
此例第一次執行時,會於C磁碟建立一個名為“abc‧bb”的新資料夾。也就是說路徑中即使有‘‧’,也可以是資料夾名稱。
例二:
File f=new File("c:/aaa/bbb/ccc");
if(!f.exists()){
 f.mkdirs();
 System.out.println("資料夾建立成功");
}else{
 System.out.println("資料夾已經存在");
}
此例第一次執行時會依照路徑參數的指定,依序建立三個新資料夾。
例三:
File f=new File("c:/");
File f2=new File(f,"ddd");
if(!f2.exists()){
 f2.mkdir();
 System.out.println("資料夾建立成功");
}else{
 System.out.println("資料夾已經存在");
}
此例是介紹兩個參數的建構方法。當我們要在同一個父資料夾中建立多個子資料夾時,可以先把父資料夾建立成獨立的File物件,再用這個File物件當參數,比較方便。
方法四:boolean isDirectory()
運算結果:呼叫此方法的File物件是否代表資料夾。
方法五:String[] list()
運算結果:取得資料夾中所有子資料夾及檔案的名稱,建構成String物件的陣列。
例一:
File f=new File("c:/WINNT");
String[] fileList=null;
if(f.isDirectory()){
f ileList=f.list();
 for(String s:fileList){
  File f2=new File(f,s);
 if(f2.isDirectory()){
  System.out.println("資料夾:" + s);
 }else{
  System.out.println("檔案:" + s);
 }
}
}else{
 System.out.println("資料夾不存在");
}
列印結果:資料夾WINNT中的所有子資料夾及檔案的名稱。讀者可以自行變更路徑參數測試。
因為File物件可以代表檔案或資料夾,所以判斷它到底代表資料夾,還是檔案?就成為很重要的工作。
 
方法六:File[] listFiles()
運算結果:取得資料夾中所有子資料夾及檔案的名稱,建構成File物件的陣列。
 
方法七:boolean delete()
運算結果:刪除指定路徑的資料夾或檔案。但若資料夾中還有子資料夾或檔案時,將不會執行刪除的動作,運算結果便為fasle。
1-3 檔案管理
方法一:boolean createNewFile()
運算結果:依建構File物件的路徑參數,在實體磁碟中建立一個新的檔案,若建立成功則運算結果為true。若實體磁碟中已存在相同名稱的檔案,將不會建立而且運算結果為false。
例一:
File f=new File("Test.yung");
if(!f.exists()){
 if(f.createNewFile()){
 System.out.println("已建立新檔案");
}else{
 System.out.println("檔案建立失敗");
}
}else{
 System.out.println("檔案已經存在");
}
本例示範的要點有:1、副檔名可以自訂。2、建立檔案前除了了解檔案是否已經存在之外,也必須注意其他因素造成的建立失敗(例:記憶體空間不足)。3、若路徑不是以磁碟機代號開頭,則表示是置放本執行檔的資料夾。
 
方法二:static File createTempFile(String prefix, String suffix)
運算結果:建立暫存檔。此檔會在程式結束時,自動刪除。
例一:
File f=File.createTempFile("abc","ddd");
if(f.exists()){
 System.out.println("已建立新檔案:" + f.getName()); 
}else{
 System.out.println("檔案建立失敗");
}
此方法為類別方法成員,可以用類別名稱呼叫。Java會用第一個參數及第二個參數再加上Java產生的編碼來串聯成暫存檔的檔案名稱(可避免檔案名稱重複)。
 
例二:
File f=File.createTempFile("abc","ddd");
if(f.exists()){
 System.out.println("已建立新檔案:" + f.getName());
}else{
 System.out.println("檔案建立失敗");
}
File f2=File.createTempFile("abc","ddd");
if(f2.exists()){
 System.out.println("已建立新檔案:" + f2.getName());
}else{
 System.out.println("檔案建立失敗");
}
本例中的兩個File物件的建構參數都一樣,但是建立的檔案名稱是不一樣的。
方法三:boolean canRead()
運算結果:檔案內容是否可以讀取。
方法四:boolean canWrite()
運算結果:是否可以輸出資料至檔案中。
方法五:String getName()
運算結果:取得檔案名稱。
方法六:String getParent()
運算結果:取得父資料夾名稱。
方法七:String getPath()
運算結果:取得完整路徑。
方法八:long length()
運算結果:取得檔案長度。
方法九:boolean isAbsolute()
運算結果:建構物件的路徑參數是否為絕對路徑。
方法十:boolean isFile()
運算結果:此物件是否代表檔案。
方法十一:boolean isHidden()
運算結果:此檔案是否隱藏。
方法十二:long lastModified()
運算結果:取得本檔案的最後修改日期。long的值可以用來建構成Date物件。
例一:
DateFormat df=DateFormat.getDateTimeInstance();
File f=new File("Test1.txt");
f.createNewFile();
System.out.println(df.format(new Date(f.lastModified())));
列印結果:格式化的檔案最後修改日期。
示範主題:各類別綜合應用的重要性。
 
方法十三:boolean renameTo(File dest)
運算結果:將執行此方法的物件代表的實體檔案的名稱,修改為參數中的File物件的檔案名稱。請注意參數是一個獨立的File物件。
例一:
File f=new File("Test1.txt");
File f2=new File("c:/Test2.txt");
f.createNewFile();
f.renameTo(f2);
執行結果:實體磁碟中,只剩下c磁碟中的Test2‧txt。
示範主題:雖然兩個File物件的路徑不一樣,但Test1‧txt一樣會被刪除。renameTo()表面上是修改檔案名稱,實際上是複製後再刪除原檔案。
 
方法十四:boolean setLastModified(long time)
運算結果:設定最後修改日期。long型別的參數可以先建構Date物件後再呼叫getTime()取得。
方法十五:boolean setReadOnly()
運算結果:設定檔案為唯讀。
 
2. FileInputStream
File類別的物件只能讓我們取得檔案的相關資訊,並不能讓我們取得檔案內容。要取得檔案內容必須使用java‧io中的另一個類別FileInputStream。
2-1 建構物件
建構方法一:FileInputStream(File file)
以File物件為參數,建構FileInputStream物件。
例一:
FileInputStream fis=null;
File f=new File("abc.dd");
if(f.exists()){
 fis=new FileInputStream(f);
}else{
 System.out.println("檔案不存在");
}
示範主題:以File物件為參數,可以先檢測檔案屬性,避免發生錯誤。
 
建構方法二:FileInputStream(String name)
以字串指定檔案路徑來建構物件。雖然比較省事,但因為沒有先行檢測檔案,可能會產生預期外之錯誤。
例一:
FileInputStream fis=new FileInputStream("abc.dd");
示範主題:以檔案路徑為建構方法的參數。
 
2-2 重要方法
方法一:int read()
運算結果:讀取檔案中一個byte的值,並將讀檔指標往後移一位。若讀到的值為-1,表示已經讀到檔案尾端。
例一:
import java.io.*;
public class JavaTest{
 public static void main(String[] args)throws IOException{
  FileInputStream fis=new FileInputStream("JavaTest.java");
  int i=0;
  while(true){
   i=fis.read();
   if(i==-1)
    break;
   System.out.print((char)i);
  }
  fis.close();
 }
}
執行結果:本例執行時,會將上述程式碼列印在DOS視窗中。
 
例二:
import java.io.*;
public class JavaTest{
 public static void main(String[] args)throws IOException{
  FileInputStream fis=new FileInputStream("JavaTest.java");
  int i=0;
  while((i=fis.read())!=-1){
   System.out.print((char)i);
  }
  fis.close();
 }
}
執行結果:本例執行結果同例一。主要是示範迴圈的寫法。
 
方法二:int read(byte〔〕 b)
運算結果:檔案內容全部讀入到byte型別的陣列b之中,並產生一個int型別的值,來記錄確實讀入的數量。便利性是一次讀入全部內容,可以不必用迴圈,但必須先知道檔案大小。
例一:
import java.io.*;
public class JavaTest{
 public static void main(String[] args)throws FileNotFoundException,IOException{
  File f=new File("JavaTest.java");
  FileInputStream fis=new FileInputStream(f);
  byte[] byteArray=new byte[(int)f.length()];
  fis.read(byteArray);
  String fileContent=new String(byteArray);
  System.out.println(fileContent);
  fis.close();
 }
}
示範主題:1、建立byte陣列時,陣列長度必須是int型別的值,而f‧length()的運算結果為long型別,所以必須用(int)強制轉換。2、byte陣列可以做為String建構方法的參數,來建構String物件,進而對檔案內容進行編修。
方法三:void close()
運算結果:關閉檔案,不產生任何值。建議在讀完檔後,應立刻將檔案關閉。
 
3. FileOutputStream
要把程式中的資料,輸出到檔案之中,便必須建立FileOutputStream物件。但是,可以指定是以覆蓋或加入的方式輸出。
3-1 建構物件
建構方法一:FileOutputStream(File file)
以File物件為參數建構物件,輸出的內容會覆蓋檔案中的原始內容。
建構方法二:FileOutputStream(File file,boolean append)
除了用File物件為參數,還多了一個布林值參數。這個布林值參數用來標示檔案內容的輸出方式。若參數值為true表示輸出的內容會加在檔案原始內容後方;若參數值為false表示輸出的內容會覆蓋檔案的原始內容。
建構方法三:FileOutputStream(String name)
建構方法四:FileOutputStream(String name, boolean append)
3-2 重要方法
方法一:void write(int b)
將參數b之值寫至檔案中。
例一:
File f=new File("Test.txt");
FileOutputStream fos=null;
if(!f.exists())
 f.createNewFile();
fos=new FileOutputStream(f);
fos.write(48);
fos.close();
執行結果:工作目錄中會新增一個檔案“Test‧txt”,檔案內容為0(因為48是字元‘0’的字元碼)。但是此例無論執行幾次,檔案中的0都只有一個,不會增加。因為每次執行時的輸出,均會覆蓋檔案的原始內容。
特別注意:程式碼末端必須呼叫close(),否則輸出會失敗。
例二:
File f=new File("Test.txt");
FileOutputStream fos=null;
if(!f.exists())
 f.createNewFile();
fos=new FileOutputStream(f,true);
fos.write(48);
fos.close();
執行結果:每執行一次,檔案中的0便會多一個。
 
方法二:void write(byte[] b)
將陣列中的值全部輸出至檔案中。
例一:
File f=new File("Test.txt");
FileOutputStream fos=null;
if(!f.exists())
 f.createNewFile();
fos=new FileOutputStream(f,true);
String s="Java 5.0 SCJP";
fos.write(s.getBytes());
fos.close();
執行結果:將“Java 5‧0 SCJP”輸出至檔案中。write方法中的參數s‧getBytes()的運算結果是一個byte型別的陣列。
 
例二:
FileOutputStream fos=new FileOutputStream("abc.txt");
String s="許大笨";
fos.write(s.getBytes());
fos.close();
特別注意:用來建構FileOutputStream物件的檔案如果不存在,在執行write()時,會自動建立該檔案。但是,若是用來建構FileInputStream物件的檔案不存在,而執行read()時,會產生執行時期錯誤。
方法三:void close()
輸出完畢後,應立刻呼叫本方法,關閉檔案。
 
4. FileReader 與 FileWriter
FileInputStream與FileOutputStream的物件,在執行讀入與輸出時,都是以一個byte為單位,逐一執行。對一般檔案而言是 可以的,但是對純文字檔而言,因為Java中每個字元都是兩個byte,拆開成一個byte、一個byte來執行,不但效率低,而且容易錯誤。
針對純文字檔,Java提供了FileReader及FileWriter兩個類別。它們建構的物件執行讀入或輸出時是以兩個byte(char)為單 位。它們的建構方式分別和FileInputStream及FileOutputStream的建構方式差不多,請自行測試。
至於重要方法方面:FileReader物件的read()中的參數,是char型別的陣列,不是byte型別的陣列。FileWriter物件的write()中,可以直接用String物件當參數輸出,不必再取得byte型別的陣列。
請各位讀者自行將FileInputStream及FileOutputStream的範例,修改為FileReader及FileWriter測試
 
5. Buffered
本章目前介紹的四個輸入、輸出的類別,都是直接把資料在檔案與程式之間傳送。為了作業方便,Java另外提供了緩衝類別,這些類別的物件建有緩衝區,可以在資料傳達至目的地前先暫存於緩衝區。
本書要介紹的緩衝類別共有:BufferedInputStream、BufferedOutputStream、BufferedReader以及BufferedWriter四種。
5-1 建構物件
四種緩衝類別物件的建構有一個特點:只接受輸入或輸出的物件為參數,不可以用File物件或String物件為參數。也就說:必須先建構輸入或輸出的物件,再以輸入或輸出的物件為參數來建構緩衝類別物件。
例:BufferedReader br=new BufferedReader(new FileReader("JavaTest.java"));

FileReader fr=new FileReader("JavaTest.java");
BufferedReader br=new BufferedReader(fr);
必須用Reader物件才能建構BufferedReader物件,其他緩衝類別,也是如此。
5-2 重要方法
方法一:String readLine()
運算結果:BufferedReader的方法,一次讀取一行資料,並建構為String物件。若String物件的內容為null表示已讀至檔案尾端。
例一:
import java.io.*;
public class JavaTest{
 public static void main(String[] args)throws FileNotFoundException,IOException{
  FileReader fr=new FileReader("JavaTest.java");
  BufferedReader br=new BufferedReader(fr);
  StringBuffer fileContent=new StringBuffer();
  String temp=null;
  while((temp=br.readLine())!=null)
   fileContent.append(temp + "\n");
  System.out.println(fileContent);
 }
}
示範主題:1、示範readLine()的使用。2、再一次提醒讀者,運算字串值的類別不只一個,應該視狀況使用適當的類別。3、readLine()一次讀取一行資料,並不會自動加入換行符號,所以必須自己加上“\n”。
 
方法二:void flush() 
運算結果:BufferedWriter及BufferedOutputStram都有宣告的方法,用來將暫存在緩衝區的資料,送至目的地。並清空緩衝區。
 
方法三:void newLine()
運算結果:BufferedWriter的方法,用來送出換行符號至目的地。
 
6. 認證重點
6-1 File
  • 财 File建構的物件是一個Java的物件,是儲存在特定記憶體區塊的資料,不是一個實質的作業系統中的檔案。它只是一個可以代表某一個檔案或資料夾的物件。
  • 财 建構File物件。注意事項有二:1、路徑分隔符號是‘/’,不是‘\’。2、若不是以磁碟機代號起頭,則預設為執行檔的所在位置。
  • 财 mkdir()與mdkirs的差異性。
  • 财 File類別的物件只能讓我們取得檔案的相關資訊,並不能讓我們取得檔案內容。
6-2 FileInputStraeam
  • 财 可以用File物件或String物件為參數建構FileInputStream物件,但執行read()時,若指定的檔案不存在,會產生執行時期錯誤。
6-3 FileOutputStream
  • 财 可以用File物件或String物件為參數建構FileOutputStream物件,但執行write()時,若指定的檔案不存在,會自動建立該檔案。
  • 财 建構方法中,可以指定檔案內容的輸出方式。
6-4 FileReader 與 FileWriter
  • 财 針對純文字檔,Java提供了FileReader及FileWriter兩個類別。它們建構的物件執行輸入或輸出時是以兩個byte為單位。
6-5 Buffered
  • 财 緩衝類別共有:BufferedInputStream、BufferedOutputStream、BufferedReader以及BufferedWriter四種。
  • 财 四種緩衝類別物件的建構有一個特點:只接受輸入或輸出物件為參數,不可以用File物件或String物件為參數。

沒有留言:

張貼留言