第 14 章 檔案與串流 14.1 檔案概論 14.2 串流概論 14.3 File(檔案/資料夾)類別 14.4 字元資料的存取 第 14 章 檔案與串流 14.1 檔案概論 14.2 串流概論 14.3 File(檔案/資料夾)類別 14.4 字元資料的存取 14.5 位元組資料的存取 14.6 實例
從鍵盤、滑鼠或其他周邊設備所輸入的資料,都暫時儲存於主記憶體中。當結束程式執行時,這些資料都會消失,下次要應用時需重新輸入。為了要節省重新輸入資料的時間或避免重要資料遺失,最好將這些資料存放在檔案中。在有需要的時侯,程式還可以將檔案內的資料讀取出來,或再將更新過的資料存入檔案中。 Java語言與檔案之間的溝通,是讓一串串的資料在電腦記憶中進進出出,形成了「串流」(stream),可貼切描述出資料在程式與檔案之間相互流動的情形。
14.1 檔案概論 程式與資料最好分別存檔。當資料有異動時,只要修改資料檔案的內容即可,不會影響程式檔案執行;同時相同的程式檔案也可以處理不同資料檔案。例如學校成績系統可以處理各科系、各班級的成績資料。系統只有一個,但是不同的老師都可以利用此系統來處理自己科目的成績資料。 檔案可分成「文字檔」與「非文字檔」兩種。文字檔是可以直接閱讀,即一般人可以看得懂的,如:程式原始碼(*.java)、HTML程式碼。而非文字檔是一般人看不懂的字碼,當然是無法直接閱讀的,如:音樂檔、圖形檔、編譯後的類別檔。
14.2 串流概論 Java是以「串流」的方式來處理輸入與輸出的資料,而串流可以分成「輸入串流」與「輸出串流」。輸入串流包括從鍵盤輸入的資料、從檔案讀取的輸入資料…。輸出串流包括將資料處理的結果存入檔案、將資料處理的結果列印出來、將資料處理的結果顯示在螢幕上…。 串流的資料可由字元(char)或位元組(byte)組成,因此在資料處理上可分為「字元串流」與「位元組串流」。其中字元串流是用來處理16個位元的Unicode資料;而位元組串流是用來處理8個位元(bit)的資料。 在撰寫Java程式碼若遇到使用串流時,會用到 java.io 套件裡的類別,因此在程式碼的開頭要載入 java.io 套件,如下:
在上一章,因為讀取音樂檔或多媒體檔,我們有很多機會接觸到輸入串流,也接觸到相伴產生的問題,即是無法順利取得輸入串流時,會丟出 Exception 例外,要用 try … catch 來捕捉,或用throws Exception來處理。無論處理的輸入串流或是輸出串流,都會有處理例外的問題。如果要詳細的區分,這種例外類別是屬輸出入會發生的例外,其例外類別應為IOException,只是Exception 它是可以代表所有的例外。
14.3 File(檔案/資料夾)類別 當我們使用Word應用程式編輯文件時,該文件最終會以 *.doc 的格式被Word程式儲存成文件檔案。而該檔案在儲存前,使用者必須要先尋找或建立一個資料夾來存放這個檔案。 上面的敘述提到了資料夾與檔案,在Java語言中,我們可以使用File類別的物件來指定或建立資料夾與檔案實體。在這裡要注意的是,File類別並不提供開啟檔案、關閉檔案或進行檔案讀寫資料的功能。
1. File (String pathname) 建立資料夾或檔案物件,用來指定資料夾或檔案實體。此時尚未包 含建立資料夾或檔案實體。 建立資料夾或檔案物件,用來指定資料夾或檔案實體。此時尚未包 含建立資料夾或檔案實體。 【簡例】 File myDir = new File("D:\\Java2"); 或 File myDir = new File("D:/Java2"); 「\\」的第一個「\」為跳脫字元,第二個「\」為反斜線字元。 建立資料夾物件myDir,myDir物件所指定的資料夾標題文字為『Java2』。而這資料夾實體將來會被建立在 D:\ 路徑中(D為磁碟機),目前尚未到建立資料夾實體的階段。
1. File (String pathname) 建立資料夾或檔案物件,用來指定資料夾或檔案實體。此時尚未包 含建立資料夾或檔案實體。 建立資料夾或檔案物件,用來指定資料夾或檔案實體。此時尚未包 含建立資料夾或檔案實體。 【簡例】 File myDir = new File("D:\\Java2"); 或 File myDir = new File("D:/Java2"); 「\\」的第一個「\」為跳脫字元,第二個「\」為反斜線字元。 建立資料夾物件myDir,myDir物件所指定的資料夾標題文字為 『Java2』。而這資料夾實體將來會被建立在 D:\ 路徑中(D為磁碟 機),目前尚未到建立資料夾實體的階段。 【簡例】 File myFile = new File("D:\\try.txt"); 或 File myDir = new File("D:/try.txt"); 建立檔案物件myFile,myFile物件所指定的檔案標題文字為『try.txt』。而 這檔案實體將來會被建立在即有的 D:/ 路徑中,目前尚未到建立檔案實體 的階段。 建議資料夾名稱不要有副檔名,而檔案名稱宜取有意義的副檔名。
2. File (File parent, String child) 【簡例】 File childDir = new File(myDir, "next"); 建立子資料夾物件childDir,指定子資料夾標題文字為『next』。而這子資 料夾實體將來會被建立在myDir資料夾物件指定的資料夾路徑中(如已建 立的『D:\Java2』)。 【簡例】 File childFile = new File(myDir, "try2.txt"); 建立檔案物件childFile,指定檔案標題文字為『try2.txt』。而這檔案實體將 來會被建立在myDir資料夾物件指定的資料夾路徑中(如已建立的 『D:\Java2』)。
3. File (String parent, String child) 在即有的父資料夾路徑中建立子資料夾或檔案物件,目前只是指定子資料夾或檔案實體。尚未到建立資料夾或檔案實體的階段。 【簡例】 File childDir = new File("D:\\Java2", "next2"); 或 File childDir = new File("D:/Java2", "next2"); 建立子資料夾物件childDir,指定子資料夾標題文字為『next2』。而這子 資料夾實體將來會被建立在已建立的 D:\Java2 路徑中。 【簡例】 File childFile = new File("D:\\Java2", "try2.txt"); 或 File childFile = new File("D:/Java2", "try2.txt"); 建立檔案物件childFile,指定檔案標題文字為『try2.txt』。而這檔案實體將來會被建立在已建立的 D:\Java2 路徑中。
二、File類別的方法 1. boolean mkdir() 在指定的路徑中建立資料夾。若路徑不存在,則建立失敗。若資料夾已存在,不重新建立。當建立成功時,傳回true;建立失敗時,傳回false。 2. boolean mkdirs() 在指定的路徑中建立資料夾,若路徑不存在,則先行建立該路徑,再建立本資料夾。若資料夾已存在,不重新建立。當建立成功時,傳回true;建立失敗時,傳回false。 3. boolean createNewFile() 在指定的路徑中建立檔案,若路徑不存在,則出現錯誤。若檔案已存在,不重新建立。當建立成功時,傳回true;建立失敗時,傳回false。
4. boolean delete() 刪除指定的檔案。 5. boolean renameTo(File pathname) 更改資料夾或檔案的標題文字。若有資料夾或檔案,傳回true;否則,傳回false。 6. boolean exists() 檢查資料夾或檔案物件是否已建立。若存在,傳回true;否則,傳回false。 7. boolean isDirectory() 檢查是否為已建立的資料夾物件。若是,傳回true;否則,傳回false。
8. boolean isFile() 檢查是否為已建立的檔案物件。若是,傳回true;否則,傳回false。 9. String getName() 以字串型別取得資料夾或檔案標題文字,不包含路徑。 10. String getPath() 以字串型別取得資料夾或檔案包含路徑的完整標題文字。 11. String getParent() 以字串型別取得資料夾或檔案的上層路徑標題文字。
1. 行04~05:建立資料夾物件myDir,myDir物件所指定的資料夾標題文字為『D:\Dir1』。 2. 行06~09:檢查磁碟機與資料夾物件是否已存在或尚未建立。 3. 行10~14:檢查資料夾物件是否建立成功。若該資料夾物件之前已被建立而存在著,則不會重新建立。
1. 行04~05:建立檔案物件myFile,myFile物件所指定的檔案標題文字為『test1.txt』。 2. 行06~09:檢查檔案物件是否已存在或尚未建立。 3. 行10~14:檢查檔案物件是否建立成功。若該檔案物件之前已被建立而存在著,則不會重新建立。
14.4 字元資料的存取 14.4.1 將字元資料寫入檔案 字元資料流為16 bits的Unicode文字串流,即雙位元組資料。 14.4.1 將字元資料寫入檔案 將字元資料寫入檔案,需使用FileWriter與BufferedWriter類別,這種處理字元串流的類別物件 稱為「串流物件」。其建構子及常用方法如下:
1. FileWriter(File myFile) myFile為檔案類別的物件名稱,參閱14.3節說明。 【簡例】 File myFile = new File(“D:/Dir1/java/stu.txt”); FileWriter fileWrite = new FileWriter(myFile); 2. FileWriter(String filename) filename為檔案名稱,可包含檔案所在磁碟機、資料夾與檔名。 建立一個可供寫入字元資料的輸出串流物件,用來開啟輸出的字 元資料檔,而同名稱的檔案會被覆蓋掉。 【簡例】 FileWriter fileWrite = new FileWriter(”D:\\Dir1\\java\\stu.txt”); 或 FileWriter fileWrite = new FileWriter(”D:/Dir1/java/stu.txt”); 其中 fileWrite 為串流物件。建立 student.txt 字元資料檔案,如果該檔案已存在,會被覆蓋。
3. FileWriter(String filename, Boolean append) append若設為true,則寫入的資料會加在原檔案後面,若設為 false,則原檔案內容會被覆蓋不見,新資料由最前面開始寫入。預設 值為false。 二、BufferedWriter建構子: 1. BufferedWriter(FileWriter fWriter) 建立記憶體緩衝區,準備輸出串流到檔案,讓資料的操作增加效能。 【簡例】 開啟緩衝區輸出串流。 2. BufferedWriter(FileWriter fWriter, int size) 建立記憶體緩衝區輸出串流,並設定緩衝區佔用記憶體的大小,預設 值為512 Bytes。 FileWriter fileWrite = new FileWriter(”D:/Dir1/java/stu.txt”); BufferedWriter fileOut = new BufferedWriter(fileWrite);
三、方法: 1. void write(String str) 將str字串寫入輸出串流。過程是先存於緩衝區,再由緩衝區寫入檔案中。 2. void write(int c) 將單一字元寫入輸出串流。 3. void newLine() 寫入換行字元。 4. void flush() 清理緩衝區並釋放記憶空間,若緩衝區內還有資料,會全部寫入檔案中。 5. void close() 關閉輸出串流,即關閉檔案。
1. 在主控制台模式下編譯及執行的情形,看不出有任何執行結果。
2. 開啟「D:\Dir1\java」資料夾,觀察該資料夾內是否有stu.txt檔。
3. 用「記事本」應用程式來開啟 stu.txt,結果如下:
3. 行08~09:若將檔案物件childFile改為字串"stu.txt" ,即第06行改為 1. 行04~05:建立資料夾物件myDir,所指定的資料夾標題文字為『D:\Dir1\java』。若路徑不存在,不能使用 mkdir() 方法,需使用 mkdirs() 方法,先行建立路徑,再建立本資料夾。 2. 行06:在即有的父資料夾myDir物件中(路徑為D:\Dir1\java)建立檔案物件childFile,其檔案標題為『stu.txt』。 3. 行08~09:若將檔案物件childFile改為字串"stu.txt" ,即第06行改為 則該「stu.txt」會儲存於J14_4_1.class同一資料夾內。 4. 行10:寫入一筆資料。 5. 行11:寫入換行字元,使往下移一行。 6. 行14:清理緩衝區。若緩衝區內還有資料,會全部寫入檔案中。 7. 行15:關閉檔案。 8. 行17~19:若路徑有錯或無法寫入此檔案,會出現「檔案處理有誤!!」訊息。 File childFile = new File(myDir, "stu.txt");
14.4.2 從檔案讀取字元資料 若要由檔案中讀出字元資料,需使用FileReader與BufferedReader類別。其建構子及常用方法如下: 一、FileReader建構子: 1. FileReader(File myFile) myFile為檔案類別的物件名稱,參閱14.3節說明。 【簡例】 2. FileReader(String filename) 建立一個可供讀取字元資料的輸入串流物件,用來從指定的檔案取出 資料。 File myFile = new File(“D:/Dir1/java/stu.txt”); FileReader fileRead = new FileReader(myFile); FileWriter fileWrite = new FileWriter(”D:/Dir1/java/stu.txt”);
二、BufferedReader建構子: 1. BufferedReader(FileReader fRead) 建立記憶體緩衝區,準備輸入串流給程式處理,讓資料的操作增加效能。 【簡例】 FileReader fileRead = new FileReader(”D:/Dir1/java/stu.txt”); BufferedReader fileIn = new BufferedReader(fileRead); 開啟緩衝區輸入串流。 2. BufferedReader(FileWReader fRead, int size) 建立記憶體緩衝區輸入串流,並設定緩衝區大小,預設值為 512 Bytes。
三、方法: 1. String readLine() 讀取一行字串。資料由檔案取出到緩衝區,再由緩衝區取出給 程式處理。 2. int read() 讀取單一字元。 3. long skip(long n) 跳過n個字元。 4. void close() 關閉輸入串流,即關閉檔案。
1. 行12:讀取一行字串資料。 2. 行13:若所寫資料為空字串,表示後面沒有資料。 3. 行16:關閉檔案。
14.5 位元組資料的存取 位元組資料流為8 bits的位元組串流。讀出與寫入需要使用FileInputStream與FileOutputStream類別。其建構子及常用方法如下: 一、建構子: 1. FileInputStream(String filename) 建立一個可供讀取位元組資料的串流物件。 2. FileOutputStream(String filename) 建立一個可供寫入位元組資料的串流物件,而同名稱的檔案會 被覆蓋掉。
二、方法: 1. int available() 取得輸入位元組資料串流所佔的位元組大小。 2. int read(byte[ ] b) 將所讀取的輸入位元組資料串流存放到位元組陣列 b 中。 3. int write(byte[ ] b) 將存放在位元組陣列 b 內的資料寫入到輸出串流物件。 4. void close() 關閉位元組串流。
1. 在主控制台模式下編譯完後,鍵入『dir』指令。請留意,目前資料夾內共有3個檔案,其中圖形檔案名稱只有一個,檔名為「babys 1. 在主控制台模式下編譯完後,鍵入『dir』指令。請留意,目前資料夾內共有3個檔案,其中圖形檔案名稱只有一個,檔名為「babys.jpg」。
2. 接著鍵入『java J14_5_1』執行程式,再鍵入『dir』指令。結果資料夾內已有4個檔,其中圖形檔增加一個,其檔名為「複製- babys.jpg」。
1. 行06:取得輸入串流。並將讀取多媒體檔案「babys.jpg」的位元資料,指派給串流物件fileIn。 2. 行07:取得輸入串流所佔的位元組大小,並建立一個同位元組元素數量的data陣列。 3. 行08:將輸入串流存放到data陣列。 4. 行10:取得輸出串流。即建立一個可供寫入位元資料的串流物件,預定要將串流輸出的檔案名稱為「複製- babys.jpg」。若有同名稱的檔案會被覆蓋掉。 5. 行11:將存放在位元組陣列data內的資料寫入到輸出串流物件。
14.6 實例
1. 行07~17:先逐行讀取「poem1.txt」檔案中的字元資料,然後逐行寫入「poem3.txt」檔案中。 2. 行18~27:接著再逐行讀取「poem2.txt」檔案中的字元資料,繼續寫入「poem3.txt」檔案中,使資料接續在「poem1.txt」資料的後面。
2. 再使用輸入對話框先後輸入二筆學生資料。 4. 若不再輸入資料,直接按 鍵表示結束輸入資料。
4. 接著在螢幕會顯示已輸入的資料,並計算平均成績,然後結束程式執行。 5. 再重新執行程式,使用輸入對話框先後輸入相同資料夾與檔案標題。 6. 輸入一筆新資料。若不再輸入資料,直接按 鍵表示結束輸入資料。
7. 接著,螢幕會顯示資料,新資料加在舊資料的後面。然後結束程式執行。
1. 行01:載入 java.util.* 套件,目的在提供第39~40行有關StringTokenizer 類別的使用。 2. 行07:若沒有輸入資料夾名稱,則檔案存於目前磁碟機最上層。 3. 行10:若沒有輸入檔案名稱,則顯示「存檔路徑有誤!!」。 4. 行18:若輸入資料為空字串,表示不再輸入資料。 5. 行38:若檔案中沒有資料,則跳離迴圈。 6. 行39~44:以”,”為分解子字串,然後分別轉為姓名字串與國文、英文整數分數,計算其平均分數,然後顯示出來。(參考4.8節)