第 7 章 java的檔案處理
DESCRIPTION
第 7 章 Java的檔案處理. Java 2 程式設計入門與應用. 目錄. 7-1 認識檔案處理 7-2 資料夾與檔案處理 7-3 文字檔的輸入與輸出 7-4 二進位檔的輸入與輸出 7-5 隨機檔 7-6 標準輸入與輸出 溫故知新 自我突破習題. 7-1 認識檔案處理. 7-1.1 檔案類型與資料流 7-1.2Java 的檔案處理類別. 前言:. 所謂的檔案就是儲存在電腦磁碟中的資料,包含文字、圖片、影片、聲音等格式,利用程式將這些檔案輸入與輸出( Input/Output ,簡稱 I/O )就稱為檔案處理。. - PowerPoint PPT PresentationTRANSCRIPT
第 7章 Java的檔案處理
Java 2 程式設計入門與應用
2
目錄
7-1 認識檔案處理7-2 資料夾與檔案處理7-3 文字檔的輸入與輸出 7-4 二進位檔的輸入與輸出 7-5 隨機檔 7-6 標準輸入與輸出溫故知新自我突破習題
3
7-1 認識檔案處理
7-1.1 檔案類型與資料流7-1.2 Java 的檔案處理類別
4
前言:
所謂的檔案就是儲存在電腦磁碟中的資料,包含文字、圖片、影片、聲音等格式,利用程式將這些檔案輸入與輸出( Input/Output ,簡稱 I/O )就稱為檔案處理。
5
7-1.1 檔案類型與資料流
電腦的檔案有多種儲存格式,大致上可將檔案分為文字檔( Text File )與二進位檔( Binary File )兩種格式。
所謂文字檔就是可以在文字編輯器中閱讀與編輯的檔案,例如:文字檔案、 Java 程式檔案;而二進位檔在文字編輯器中開啟則是一群無法辨識的亂碼與符號,例如:影像檔、聲音檔與圖片檔……等。
6
文字檔與二進位檔的比較:
文字檔與二進位檔的差別在於讀取和寫入檔案時的轉換方式不同。
在 Windows 作業系統中,每個文字檔案都有反回( Carriage Return ,簡稱 CR )字元、換行( Line Feed ,簡稱 LF )字元與結尾( End of File ,簡稱 EOF )字元,其中 CR 的十六進位表示法為 0d , LF 的十六進位表示法為 0a ,而 EOF 的十六進位表示法為 1a (也就是在鍵盤上按下組合鍵 + 的意思)。
7
在文字檔與二進位檔讀取與寫入資料的情形說明如下:1. 文字檔:讀取文字檔時若遇到 CR/LF 字元會自動轉換成只有 LF
字元( "\n" ),若遇到十六進位 1a 則會自動轉換成 EOF 字元;而寫入資料到文字檔時會將 LF 字元轉換成 CR/LF 字元,將 EOF字元轉換成十六進位 1a 。
2. 二進位檔:二進位檔是沒有處理過的位元組資料,不會像文字檔一樣自動轉換字元,所以讀取與寫入檔案的資料完全相同。在前面曾經提過二進位檔包括影像檔、聲音檔與圖片檔……等,所以在文字編輯器中會是一堆無法辨識的亂碼。
8
資料流: 資料流( Data Stream )是一系列排列好的位元組資
料,在 Java 中以資料流來輸入與輸出檔案,會將資料流轉換成相對應的資料格式,例如:文字、圖片、聲音與影像等。
在 Java 中輸入表示從檔案中讀取資料到記憶體中( Read ),而輸出剛好相反,將記憶體中的資料輸出到檔案中( Write )。
9
7-1.2 Java的檔案處理類別
在 Java 的 java.io 類別庫( java.io 中的類別皆繼承自 java.lang.Object 類別)中提供了多個子類別,可以進行檔案處理,值得注意的是,在使用這些類別前必須先以 import 指令載入 java.io 類別庫,並且處理 IOException 所拋出的例外情況。
10
下圖為 java.io類別庫中較常用的檔案處理類別:
11
上面的類別說明如下:
1. File 類別:處理作業系統中的檔案與資料夾,包含建立與刪除等動作。
2. BufferReader/BufferWriter 類別:讀取 / 儲存緩衝區資料,緩衝區可以暫存從外部裝置讀取的資料(例如:前面教的在程式執行結果視窗中輸入資料,就是讀取使用者輸入到緩衝區中的資料),或是要儲存到外部裝置的資料。
3. FileReader/FileWriter 類別:讀取 / 儲存文字檔案。4. FileInputStream/FileOutputStream 類別:讀取 / 儲存
文字或二進位檔案。5. RandomAccessFile 類別:讀取或儲存隨機檔案。
12
7-2 資料夾與檔案處理
7-2.1 查看資料夾與檔案的資訊7-2.2 處理資料夾與檔案
13
前言:
java.io 類別庫的 File 類別提供了許多函式,可以對作業系統中的資料夾與檔案進行操作,以及查看資料夾與檔案的各種資訊。
14
7-2.1 查看資料夾與檔案的資訊
在 Java 中若要查看資料夾與檔案的資訊,必須要先建立 File 物件,其語法如下:
上述語法參數的資料夾或檔案名稱若與 Java程式在同一層目錄,直接撰寫資料夾或檔案名稱即可,例如: Java.txt ;若是在不同目錄底下,就必須指定完整的路徑,例如: C:\\Java.txt (因為字串裡單獨的反斜線 \ 會被視為跳脫字元,所以必須用兩個反斜線 \\ 來分隔每一層目錄)。
15
下面是 File類別中查看資料夾與檔案資訊的函式:
16
例如下面的程式碼:
17
18
上述程式碼分析如下:
1. 第 3 行必須在 main() 方法的參數列後面拋出IOException 例外處理。
2. 第 9 行判斷若 File 物件是資料夾就執行第 10~15 行的程式碼,否則在第 17 行判斷 File 物件是否為檔案。
3. 第 12 行宣告字串陣列 s 儲存資料夾中的清單名稱,並在第 13~15 行利用 for 迴圈逐一顯示所有資料夾與檔案的名稱。
19
完成後,執行程式的畫面顯示如下:
20
7-2.2 處理資料夾與檔案
在處理資料夾與檔案前也要先建立 File 物件,下面是 File 類別中處理資料夾與檔案資訊的函式:
21
例如下面的程式碼:
22
23
上述程式碼分析如下:
1. 第 5~8 行宣告四個 File 物件, f1~f3 是檔案,f4 是資料夾。
2. 第 9~10 行判斷當 ch07_02_02a.txt 存在時,就修改檔名為 ch07_02_02.txt 。
3. 第 11~12 行判斷當 ch07_02_02b.txt 存在時,就刪除該檔案。
4. 第 13~14 行判斷當 ch07_02_02 資料夾存在時,就建立 ch07_02_02.txt ;若不存在,則建立 ch07_02_02 資料夾。
24
完成後,畫面顯示如下: :
完成後執行程式,會發現 Java 程式的目錄底下的 ch07_02_02a.txt改名為 ch07_02_02.txt ,ch07_02_02b.txt已經被刪除,並且自動建立 ch07_02_02 資料夾,畫面顯示如下:
25
26
7-3 文字檔的輸入與輸出
7-3.1 檔案處理函式7-3.2 從文字檔案讀取資料— FileReader 類別7-3.3 將資料寫入文字檔案— FileWriter 類別7-3.4 逐行讀取資料— BufferedReader 類別7-3.5 逐行寫入資料— BufferedWriter 類別
27
前言:
撰寫程式時,經常需要應用文字檔的輸出入技巧,尤其要處理的資料量很多時,可以利用檔案來輸出與輸入資料,這種方式可以永久保存資料,是存取資料的重要程式技巧。
28
7-3.1 檔案處理函式 java.io 類別庫的 Reader 與 Writer 類別提供了許多檔案處理的函式,
可以讓衍生的類別使用,分別說明如下:
上面的函式可以應用在 Reader 與 Writer 類別衍生的 BufferedReader 、 BufferedWriter 、 FileReader 與 FileWriter 類別。
29
7-3.2 從文字檔案讀取資料— FileReader類別利用 FileReader 類別讀取文字檔案的資料,必須依照下面五個步驟來撰寫程式碼。
30
宣告字元陣列儲存要讀取的文字資料:
為了避免檔案中的字元數超過設定的陣列大小,可以將陣列大小設定較大的數字。
31
建立 FileReader物件:
建立 FileReader 物件時,必須指定要讀取的檔案名稱,若檔案與 Java 程式在同一層目錄,直接撰寫檔案名稱即可,例如: ch07_03_02.txt ;若是在不同目錄底下,就必須指定完整的路徑,例如: D:\\Sample\\CH07\\ch07_03_02.txt (因為字串裡單獨的反斜線 \ 會被視為跳脫字元,所以必須用兩個反斜線 \\ 來分隔每一層目錄)。
參數 " 讀取的檔案名稱 " 可以換成 File 物件,會讀取File 物件的檔案。
32
使用 read函式讀取檔案的資料:
利用 read 函式讀取檔案的資料並儲存到指定的字元陣列中,這裡宣告的整數是儲存讀取的資料長度。
33
將讀取的字元資料轉換成字串:
建立 String 物件時,必須指定轉換從索引值為0 到資料長度的資料,若不指定資料範圍,就會將當初宣告的陣列大小全部顯示出來(沒有資料的地方會以空白字元填滿)。
34
關閉檔案:
利用 close 函式可以關閉檔案,關閉檔案後就不可以再讀取檔案。
35
例如下面的程式碼:
36
上述程式碼分析如下:
1. 第 1 行必須載入 java.io 類別庫。2. 第 3 行必須在 main() 方法的參數列後面拋出 IOExce
ption 例外處理。3. 第 7 行建立 FileReader 物件,並且指定讀取的檔名為
ch07_03_02.txt ,若是該檔案不存在會做例外處理,拋出找不到檔案的錯誤訊息。
4. 第 9 行利用 read 函式讀取 ch07_03_02.txt 檔案中的資料,並將該檔案中的資料長度儲存在變數 len 中(換行以兩個跳脫字元“ \r\n” 表示,佔用 2 個資料長度,所以變數 len 的值為 131 )。
5. 第 13 行在檔案處理完成後,必須利用 close 函式關閉檔案。
37
完成後,執行程式的畫面顯示如下:
38
7-3.3 將資料寫入文字檔案— FileWriter類別利用 FileWriter 類別儲存文字檔案的資料,必須依照下面三個步驟來撰寫程式碼。
39
建立 FileWriter物件:
建立 FileWriter 物件時,必須指定要儲存的檔案名稱,有下面兩種方式(檔案名稱的寫法與建立 FileReader物件的相同):FileWriter 物件名稱 =new FileWriter(" 儲存的檔案名稱 "); :原本檔案中的資料會被覆蓋。
FileWriter 物件名稱 =new FileWriter(" 儲存的檔案名稱 ",布林值 ); :若布林值為 true ,則會將資料附加在原本資料的後面;若布林值是 false ,就會預設為覆蓋原本資料。
參數 " 儲存的檔案名稱 " 可以換成 File 物件,會儲存File 物件的檔案。
40
使用 write函式將資料寫入檔案:
要儲存的資料可以是字元、字元陣列或字串型別。
41
關閉檔案:
利用 close 函式可以關閉檔案,關閉檔案後就不可以再儲存資料。
42
例如下面的程式碼:
43
44
上述程式碼分析如下:
1. 第 7 行建立 FileWriter 物件,並指定儲存的檔名為 ch07_03_03.txt ,因為沒有設定布林值,所以若該檔案原本存在,會覆蓋原本檔案中的資料。
2. 第 9 與 11 行利用 write 函式分別將字串 s 與字元陣列 c 寫入檔案中。
3. 第 13 行利用 close 函式關閉檔案。4. 第 14~20 行利用 FileReader 物件讀取 ch07_03
_03.txt ,並顯示出來。
45
完成後,執行程式的畫面顯示如下:
46
47
48
49
7-3.4 逐行讀取資料— BufferedReader類別除了利用 FileReader 類別來讀取文字檔案,也可以利用 BufferedReader 類別先建立一個緩衝區物件,然後使用 readLine 函式逐行讀取緩衝區的資料,這樣就不用預先宣告一個字元陣列來儲存讀取的資料了。
利用 BufferedReader 類別讀取文字檔案的資料,必須依照下面三個步驟來撰寫程式碼。
50
建立 BufferedReader物件:
利用一個 FileReader 物件當做參數來建立 BufferedReader 物件,同樣地,必須指定要儲存的檔案名稱(檔案名稱的寫法與建立 FileReader物件的相同)。
51
使用 readLine函式逐行讀取資料:
宣告一個 String 字串儲存每一行讀取的資料,判斷讀取的資料是否為 null ,如果等於 null 就表示已經讀取到檔案結尾,則跳出 while迴圈。
52
關閉檔案:
利用 close 函式可以關閉檔案,關閉檔案後就不可以再讀取檔案。
53
例如下面的程式碼:
54
55
上述程式碼分析如下:
1. 第 5 行利用建立 BufferedReader 物件,參數為新建立的 FileReader 物件,並指定讀取的檔案名稱為 ch07_03_04.txt 。
2. 第 9~11 行利用迴圈每次讀取檔案 ch07_03_04.txt 中的一行資料,當讀取的資料為 null(已經讀取到檔案結尾)就跳出迴圈。
3. 第 12 行利用 close 函式關閉檔案。
56
完成後執行程式,就會在螢幕上顯示檔案中的資料,畫面顯示如下:
57
58
59
60
7-3.5 逐行寫入資料— BufferedWriter類別除了利用 FileWriter 類別來儲存文字檔案,也可以利用 BufferedWriter 類別先建立一個緩衝區物件,然後使用 write 與 newLine 函式逐行寫入資料到緩衝區,比較特別的是,必須透過flush 函式將緩衝區的資料寫入檔案,並清空緩衝區。
利用 BufferedWriter 類別儲存文字檔案的資料,必須依照下面四個步驟來撰寫程式碼。
61
建立 BufferedWriter物件:
利用一個 FileWriter 物件當作參數來建立 BufferedWriter 物件,同樣地,必須指定要儲存的檔案名稱(檔案名稱的寫法與建立 FileReader 物件的相同)。
62
使用 write函式逐行寫入資料,使用 newLine函式換行:將資料寫入緩衝區,要儲存的資料可以是字元、字元陣列或字串型別。
63
清空緩衝區的資料:
利用 flush 函式將 BufferedWriter 緩衝區中的資料寫入檔案,然後清空緩衝區的資料。
64
關閉檔案:
利用 close 函式可以關閉檔案,關閉檔案後就不可以再儲存資料。
65
例如下面的程式碼:
66
67
上述程式碼分析如下:
1. 第 9 行利用建立 BufferedWriter 物件,參數為新建立的 FileWriter 物件,並指定儲存的檔案名稱為 ch07_03_05.txt 。
2. 第 11~14 行利用 for迴圈將字串陣列 s 寫入資料,第12 行是陣列的第 i+1 個資料寫入緩衝區,第 13 行是在緩衝區中換行。
完成後執行程式,就會在 Java 程式的目錄底下自動建立 ch07_03_05.txt ,並且將指定的資料寫入檔案中,然後在螢幕上顯示檔案中的資料。
68
69
7-4 二進位檔的輸入與輸出
7-4.1 從二進位檔案讀取資料— FileInputStream 類別
7-4.2 將資料寫入二進位檔案— FileOutputStream 類別
70
前言:
7-1.1小節曾經比較過文字檔與二進位檔的不同,對於不可以被轉換的二進位資料,當然使用二進位檔案是比較恰當的。
在 Java 中利用 FileInputStream /FileOutputStream 類別可以讀取 / 儲存文字檔或二進位檔。
71
7-4.1 從二進位檔案讀取資料— FileInputStream類別利用 FileInputStream 類別讀取二進位檔案的資料,必須依照下面四個步驟來撰寫程式碼。
72
建立 FileInputStream物件:
建立 FileInputStream 物件時,必須指定要讀取的檔案名稱。
參數 " 讀取的檔案名稱 " 可以換成 File 物件,會讀取 File 物件的檔案。
73
宣告位元組陣列儲存要讀取的二進位資料:因為二進位檔的資料內容為位元組( byte ),所以要宣告位元組陣列來儲存資料,宣告位元組陣列時可以利用 FileInputStream 類別的 available 函式取得資料流的位元組數。
74
使用 read函式讀取檔案的資料:
利用 read 函式讀取檔案的資料,並儲存到指定的位元組陣列中。
75
關閉檔案:
利用 close 函式可以關閉檔案,關閉檔案後就不可以再讀取檔案。
76
例如下面的程式碼:
77
上述程式碼分析如下:
1. 第 5 行建立 FileInputStream 物件,並指定讀取二進位檔 ch07_04_01.bin 。
2. 第 7 行宣告位元組陣列 buffer 儲存要讀取的二進位資料,宣告陣列時,利用 available 函式取得檔案的大小,以配置陣列的記憶體空間。
3. 第 9 行利用 read 函式讀取二進位檔 ch07_04_01.bin的資料。
4. 第 11 行將檔案中的資料顯示出來,因為檔案的資料儲存在位元組陣列 buffer 中,所以先利用 new String將 buffer 轉換成 String 物件,然後顯示出來。
78
完成後,執行程式的畫面顯示如下:
79
7-4.2 將資料寫入二進位檔案— FileOutputStream類別利用 FileOutputStream 類別儲存二進位檔案的資料,必須依照下面三個步驟來撰寫程式碼。
80
建立 FileOutputStream物件:
建立 FileOutputStream 物件時,必須指定要讀取的檔案名稱,有下面兩種方式:FileOutputStream 物件名稱 =new FileOutputStream(" 儲存的檔案名稱 "); :原本檔案中的資料會被覆蓋。
FileOutputStream 物件名稱 =new FileOutputStream(" 儲存的檔案名稱 ",布林值 ); :若布林值為 true ,則會將資料附加在原本資料的後面;若布林值是 false ,就會預設為覆蓋原本資料。
參數 " 儲存的檔案名稱 " 可以換成 File 物件,會儲存File 物件的檔案。
81
使用 write函式將資料寫入檔案:
利用 write 函式讀取檔案的資料,並儲存到指定的位元組陣列中。
82
關閉檔案:
利用 close 函式可關閉檔案,關閉檔案後就不可再寫入檔案。
83
下面利用 FileInputStream與 FileOutputStream類別複製圖片檔案,其程式碼如下:
下面的程式碼為利用 FileInputStream 與 FileOutputStream 類別,複製圖片檔案 ch07_04_02a.jpg ,另存為 ch07_04_02b.jpg :
84
85
上述程式碼分析如下:
1. 第 5~7 行讀取圖片檔案 ch07_04_02a.jpg ,並暫存到位元組陣列 buffer 中。
2. 第 9 行建立 FileOutputStream 物件,並指定儲存的檔案為圖片檔案 ch07_04_02b.jpg 。
3. 第 10 行利用 write 函式將陣列 buffer 的資料儲存到 ch07_04_02b.jpg 。
完成後執行程式,就會複製 ch07_04_02a.jpg ,在 Java 程式的目錄底下自動建立 ch07_04_02b.jpg ,其圖片內容與 ch07_04_02a.jpg 相同。
86
87
88
7-5 隨機檔
7-5.1 循序檔與隨機檔的比較7-5.2 隨機存取檔案— RandomAccessFile 類別
89
前言:
在本節前面介紹的檔案處理都是屬於循序檔,只能循序讀取與儲存檔案,若要隨機存取檔案,則必須使用 RandomAccessFile 類別。
90
7-5.1 循序檔與隨機檔的比較
所謂循序檔( Sequential File )就是在檔案處理時,所有檔案都會按照順序來讀取與寫入。
當讀取資料時會從頭逐一讀取,直到搜尋到要讀取的資料,而寫入資料時則從最尾端開始寫入。
91
隨機檔:
隨機檔( Random File )的每一筆資料長度固定,所以可以任意存取檔案中的任何一筆資料,讀取隨機檔時可以直接跳到指定的那一筆資料,以節省讀取資料花費的時間。
92
循序檔與隨機檔主要的差別在於儲存方式的不同,如下圖所示:
93
循序檔與隨機檔的差異說明如下:
94
7-5.2 隨機存取檔案— RandomAccessFile類別在隨機檔中可以隨時跳到想要存取檔案的位置,就好像在陣列中使用索引值存取陣列元素一樣,所以可稱此索引值為檔案指標( File Pointer )。
在存取隨機檔之前必須先建立 RandomAccessFile 物件,其語法如下:
95
上述語法說明如下:
1. 參數 " 存取的檔案名稱 " 可以換成 File 物件,會儲存 File 物件的檔案。
2. 存取模式:是一個字串, "r" 表示只能讀取檔案,若檔案不存在,則會拋出 IOException 例外處理; "rw" 表示可以讀寫檔案,若檔案不存在,則會自動建立新的檔案。
96
RandomAccessFile 類別的函式 -1 :
RandomAccessFile 類別提供下面的函式來讀取檔案:
97
RandomAccessFile 類別的函式 -2 :
98
RandomAccessFile 類別的函式 -3 :
99
移動檔案指標的位置:
除了上面存取檔案的函式, RandomAccessFile類別還提供了一些函式,可以取得與調整檔案指標的位置,將檔案指標移動到想要讀取資料的位置,以達到隨機存取檔案資料的目的。
在隨機檔中,位移量( Offset )是指從檔案開頭到檔案目前位置的移動單位,當下一次存取資料時,會從位移量的位置開始存取。
100
函式說明如下:
101
下面利用 seek函式隨機讀取資料,其程式碼如下: 下面的程式碼將一個字串陣列與一個整數陣列寫入隨
機檔,然後讓使用者輸入要搜尋讀取第幾筆資料,再利用 seek 函式隨機讀取資料:
102
103
上述利用 seek函式隨機讀取資料的程式碼分析如下:1. 第 5 行建立一個可儲存與讀取的隨機檔 07_05_02.bin 。2. 第 9~13 行分別將字串與整數資料寫入檔案中。3. 第 15 行利用 getFilePointer 函式顯示目前檔案指標的位置,因為剛才將資料寫入檔案,所以目前位置會在檔案的最後面。
4. 第 16 行利用 length 函式顯示檔案的位元數,因為每個字元佔有 2 Bytes,則 name 字串陣列的每個元素佔有 6 Bytes ;而 age整數陣列的每個元素佔有 4 Bytes ,陣列中有 4 個元素,所以總共有 4*(6+4)=40 Bytes 。
5. 第 21 行移動檔案指標到第 n筆資料的位置,因為每筆資料佔有 10 Bytes ,所以將檔案指標從檔案開頭向前移動 (n-1)*10 的位置。
6. 因為沒有直接讀取字串的函式,所以第 24~26 行將姓名先暫存在 temp 字元陣列中,然後在第 28 行將字元陣列轉換成字串以顯示出來。
104
完成後,執行程式的畫面顯示如下:
105
106
7-6 標準輸入與輸出
7-6.1 從標準輸入裝置讀取資料— InputStreamReader 類別
7-6.2 在標準輸出裝置顯示資料— OutputStreamWriter 類別
107
前言:
Java 的 InputStreamReader 與 OutputStreamWriter 類別可以將位元組資料轉換成字元資料,所以適合用來處理標準輸入與輸出資料流( System 類別的 System.in 與 System.out 子類別),也就是指從程式執行畫面上讀取與顯示資料。
108
7-6.1 從標準輸入裝置讀取資料—InputStreamReader類別 InputStreamReader 類別必須配合 BufferedRead
er 類別從標準輸入裝置讀取資料,所以可以利用 BufferedReader 類別的 read 與 readLine 函式讀取資料。
109
開啟標準輸入資料流的語法如下:
110
下面的程式碼讓使用者輸入中文姓名,然候先讀取一個字元,再讀取剩下的字串:
上述程式碼必須載入 java.io 類別庫,並拋出 IOException 例外處理,然後利用 read 與 readLine 函式分別從螢幕讀取一個字元與字串。
111
完成後,執行程式的畫面顯示如下:
112
7-6.2 在標準輸出裝置顯示資料—OutputStreamWriter類別OutputStreamWriter 類別必須配合 BufferedWrit
er 類別將資料顯示到標準輸出裝置中,所以可以利用 BufferedWriter 類別的 write 函式輸出資料。
113
開啟標準輸出資料流的語法如下:
114
下面的程式碼將一個字元陣列與字串資料顯示到螢幕上:
上述程式碼利用 write 函式在標準輸出裝置(螢幕)上顯示字元陣列與字串,與利用 System.out.println 函式有相同的結果。
115
完成後,執行程式的畫面顯示如下:
116
溫故知新 -1:
117
溫故知新 -2:
118
溫故知新 -3:
119
溫故知新 -4:
120
溫故知新 -5:
121
溫故知新 -6:
122
自我突破習題 -選擇題:
123
自我突破習題 -選擇題:
124
自我突破習題 -實作題:
125
自我突破習題 -實作題:
126
自我突破習題 -實作題: