280 likes | 431 Views
副程式概念與 檔案 存取. 函式與傳值 重構觀念 串流資料 檔案資料存取. 副程式與重構概念. 重構 (Refactory) 將程式中具有特定功能的部分獨立出來成為類別方法或副程式,而主程式的部份則呼叫這些方法或副程式出來使用,其目的便是要讓程式更具結構化。 副程式 (Subprogram) 主要分為兩種, 一種是宣告為 void 而沒有傳回值的函式 (Subroutine) , 一種則是宣告為變數型態必須有 return 指令傳回計算值的函數 (Function) 。 此概念在物件導向設計裡,則衍生為類別中的方法 (Method) 。
E N D
副程式概念與檔案存取 函式與傳值 重構觀念 串流資料 檔案資料存取
副程式與重構概念 • 重構(Refactory) • 將程式中具有特定功能的部分獨立出來成為類別方法或副程式,而主程式的部份則呼叫這些方法或副程式出來使用,其目的便是要讓程式更具結構化。 • 副程式(Subprogram) • 主要分為兩種, • 一種是宣告為void而沒有傳回值的函式(Subroutine), • 一種則是宣告為變數型態必須有return指令傳回計算值的函數(Function)。 • 此概念在物件導向設計裡,則衍生為類別中的方法(Method)。 • 主程式可定義為Java類別中一個名為main的函式,其傳入的引數為一個字串陣列。 • 傳入副程式的引數除非是傳入位址,例如陣列的傳址,否則傳入的值並不會隨著副程式的計算而改變。
Example – 以Ex02_LoopControl為例重構成以下之副程式 • forLoop(): 測試for迴圈 • 測試for迴圈 • ConvertTemp(double cButtom, double cTop, int n) : 溫度對照表 • 輸入攝氏溫度上下限,分成n個刻度,建立攝氏華式溫度表 • 建立換算溫度函數(CtoF(), FtoC)計算溫度 • FoldRope(float ropeLen, float sectLen) : 繩索對折次數計算 • 輸入繩索長度ropeLen及對折後長度sectLen,印出對折次數 • errorIncrement(double iniValue, double limValue) : 浮點數迭加 • 代入iniValue初始值,連續迭加至上限值limValue,觀察累計誤差 • nFactorial(int n) : 階乘計算函數 • 代入一整數參數n後得到n!階乘之值 • Table99() : 九九乘法表 • 建立九九乘法表 • Lotto() : 樂透彩自動選號 • 副程式中改以陣列型態產生一組號碼 • 且將排序部份程式改為函數(BubbleSort) • 主程式呼叫此副程式時利用迴圈產生多組號碼
副程式重構範例 public class Ex04_Refactory { public static void main(String[] args) { System.out.println("測試for迴圈"); forLoop() ; System.out.println("溫度對照表"); ConvertTemp(20, 100, 10) ; System.out.println("繩索對折次數計算"); FoldRope(100f, 20f) ; System.out.println("浮點數迭加 – 誤差累計"); errorIncrement(0.1, 100) ; System.out.println("階乘計算"); System.out.println("5!=" + nFactorial(5)) ; System.out.println("九九乘法表"); Table99() ; System.out.println("樂透彩自動選號"); for(int i=0; i<6; i++) { System.out.println("第"+(i+1)+"組號碼"); Lotto() ; } }
public static void forLoop() {// 測試for迴圈 int total = 0; // 遞增for迴圈敘述 for (int i = 1; i <= 10; i++ ) { System.out.print("Number: " + i + " "); total += i; } System.out.println("\nSummary from 1 to 10: " + total); System.out.println(" ----------------- "); total = 0; // 重設總和變數 // 遞減for迴圈敘述 for (int i = 10; i >= 1; i-- ) { System.out.print("Number: " + i + " "); total += i; } System.out.println("\nSummary from 10 to 1: " + total); }
// 溫度對照表 public static void ConvertTemp(double cButtom, double cTop, int n) { double c = cButtom ; double f; System.out.println("C F"); // while迴圈敘述 // while ( c <= 100 ) { // f = (9.0 * c) / 5.0 + 32.0; while ( c <= cTop ) { f = CtoF(c) ; System.out.println(c + " " + f); // c += 10; c+=(cTop-cButtom)/n ; } }
// 溫度對照表 public static void ConvertTemp(double cButtom, double cTop, int n) { double c = cButtom ; double f; System.out.println("C F"); // while迴圈敘述 // while ( c <= 100 ) { // f = (9.0 * c) / 5.0 + 32.0; while ( c <= cTop ) { f = CtoF(c) ; System.out.println(c + " " + f); // c += 10; c+=(cTop-cButtom)/n ; } } public static double CtoF(double c) {// 溫度轉換公式 C -> F double f = (9.0 * c) / 5.0 + 32.0; return f ; } public static double FtoC(double f) {// 溫度轉換公式 F -> C double c = (f - 32.0) * 5.0 / 9.0 ; return c ; }
// 繩索對折次數計算 public static void FoldRope(float ropeLen, float sectLen) { int count = 0; // 計算次數 // float len = 100.0f; float len = ropeLen ; // do/while迴圈敘述 do { System.out.println(count + " Length: " + len); count++; len /= 2.0; // 對折繩索 } while ( len > sectLen ); System.out.println("Folding Number: " + count); System.out.println("Final Length: " + len); }
// 浮點數迭加 – 誤差累計 public static void errorIncrement(double iniValue, double limValue) { // double d=0.1; double d = iniValue ; double sum=0.0; // while(d<=10.0){ while(d<=limValue){ sum=sum+d; System.out.println(d +"\t"+sum + "\t" + Math.round(sum*100)/100.0); d = d + 0.1; } }
public static int nFactorial(int n) {// 階乘計算 int num = n; int prod = 1; int count = 1 ; // do/while迴圈敘述 do { System.out.println("Number: " + count); if (num == 0) break ; // 跳出迴圈 else prod *= count; count++; } while ( count <= num ); // System.out.println("5! = " + prod); return prod ; }
public static void Table99() {// 九九乘法表 // 顯示標題列 System.out.print(" "); for (int i = 1; i <= 9; i++ ) System.out.print(i + " "); System.out.println(); // 巢狀迴圈-第一層while迴圈 int row = 0, col=0 ; while (row <= 9 ) { // 顯示欄標題 System.out.print(row + " "); for (col = 1; col <= 9; col++ ) { // 第二層for迴圈 System.out.print(row + "*" + col + "="); System.out.print(row*col + " "); if ( (row*col ) < 10 && col != 1 ) System.out.print(" ");// 調整顯示位置 } row++; // 計數器變數加一 System.out.println(); } }
public static void Lotto() {// 樂透彩自動選號 int[] lo = new int[6] ; boolean check ; int k = 1; do { for(int i=0; i<6; i++) lo[i] = ((int)(Math.random() * 1000)) % 49 + 1; check = false; for(int i=0; i<6; i++) { for(int j=i+1; j<6; j++) { if(lo[i] == lo[j])check = true ; } } k++ ; } while(check) ; System.out.print("重號次數:" + k + "\n樂透號碼:") ; for(int i=0; i<6; i++)System.out.print(lo[i] + "\t") ; System.out.println() ; System.out.print("重新排序:") ; BubbleSort(lo) ; for(int i=0; i<6; i++)System.out.print(lo[i] + "\t") ; System.out.println() ; }
public static int[] BubbleSort(int[] lo) {// 氣泡排序 for(int i=0; i<lo.length-1; i++) { for(int j=i+1; j<lo.length; j++) { if(lo[i] > lo[j]) { int big = lo[i] ; lo[i] = lo[j] ; lo[j] = big ; } } } return lo ; } }
傳址與傳值 • 變數與位址資料 • 變數資料存於位址上,不同的變數可能指向同一個位址。 • 當資料值改變時,表示使用相同位址的不同變數值會一起改變。 • 唯有將不同變數指向不同位址時,變數的資料才各自獨立。 • 副程式的資料傳遞 • Java的設計上省略了C/C++的指標(pointer)運算,直接應用位址與數值的傳送存取副程式資料 • 純量變數的資料傳送是把數值傳入副程式,輸入變數和副程式引數佔不同的位址 • 陣列變數資料的傳送是把位址傳入副程式,輸入變數和副程式引數使用相同位址 • 使用副程式處理輸入引數時,如果要得到運算後的純量變數,或希望保留原始陣列,則應利用函數的傳回值進行處理 • 利用等號( = )讓不同的陣列變數對應到同一位址 • 利用陣列物件的clone()方法將陣列資料複製到不同的位址
public static void setA(int a0, int[] a) { a0 = a0 * a0 ; for(int i=0; i<a.length; i++) { a[i] = a[i] * 10 ; } } public static int getA(int a0) { return a0*a0 ; } public static int[] getB(int[] a) { int[] b = a.clone() ; /* 複製陣列, 與clone同義 int[] b = new int[a.lenght] ; for(int i=0; i<b.length; i++) { b[i] = a[i] ; } */ for(int i=0; i<b.length; i++) { b[i] = b[i] * 10 ; } return b ; } public static void main(String[] args) { int ia = 10 ; int ib = ia ; ia = 20 ; System.out.println("ia="+ia+"\tib="+ib) ; int[] a = {1, 4, 3, 5, 9} ; setA(ia, a) ; System.out.println("a[] --> changed in setA") ; for(int i=0; i<a.length; i++) { System.out.print(a[i] + "\t") ; } System.out.println("ia = " + ia) ; ib = getA(ia) ; System.out.println("Use getA to return new value - input: " + ia + "\treturn: " + ib) ; int[] b = getB(a) ; System.out.println("Use getB to return new value of input a[]\na[]\tb[]") ; for(int i=0; i<a.length; i++) { System.out.println(a[i] + "\t" + b[i]) ; } }
檔案資料處理 • 資料串流 • 串流(stream)觀念最早使用在UNIX作業系統,串流模型如同水管的水流,並沒有考慮資料來源、型態等,當程式開啟一個來源的輸入串流(例如檔案、記憶體和緩衝區等)為循序存取串流(Sequential Access Streams),如水流般依序讀取和寫入資料。 • Java提供了一套用來處理串流資訊的函式套件,Java I/O套件,全名是Java Input/Output(輸入/輸出),即應用程式的資料輸入與輸出,在Java類別函式庫(Class Library)是使用串流模型來處理資料的輸入與輸出,基本上Java串流類別分成兩大類: • 字元串流(CharacterStream) • 位元組串流(ByteStream)
字元串流(CharacterStream)- • 字元串流是是一種適合人類閱讀(Human-readable)的串流,Reader/Writer兩個類別分別讀取和寫入16位元的字元資料。 • BufferReader/BufferWriter:處理緩衝區I/O。 • InputStreamReader/OutputStreamWriter:InputStreamReader在讀取位元組資料後將它轉成字元資料,OuputStreamWriter是將字元轉換成位元組資料。 • FileReader/FileWriter:處理檔案的I/O。 • 位元組串流(ByteStream)- • 位元組串流是一種電腦格式(Machine-formatted)串流,可以讀取和寫入8位元的位元組資料,也就是處理二進位資料的執行檔、圖檔和聲音等。 • FileInputStream/FileOutputStream:處理檔案的I/O。 • DataInputStream/DataOutputStream:讀取和寫入基本資料型態的資料。 • BufferInputStream/BufferedOutputStream:處理緩衝區I/O。
檔案(File)與資料夾目錄(Directory)處理- • File物件-java.io套件提供File類別(java.io.File)建立物件,取得檔案或資料夾的相關資訊,例如取得檔名、檔案大小、檔案路徑、檔案屬性或目錄下之檔案列表等,物件宣告如下File f = new File(String str) ; • String str: 代表檔案名稱,包含絕對路徑 • 顯示資料夾資訊 • 建立資料夾目錄的方法 • 更改檔案或資料夾名稱的方法 • 刪除檔案的方法
一般文字檔案串流處理- • FileWriter物件-java.io套件提供FileWriter類別(java.io.FileWriter)建立物件,來處理文字檔案的串流,物件宣告如下FileWriter fw = new FileWriter(File f, true) ; • File f: 代表被宣告的檔案物件變數 • true: 省略時表示覆蓋檔案物件f的內容,不省略則表示從檔案尾端附加新的字元串流 • 使用FileWriter時,必須在try…catch例外處理區塊中 • 常用的檔案寫入方式File f = new File(“filename.txt”) ;FileWriter fw ;try { if(fcopy.exists()) {// 如果檔案存在則於尾部附加文字資料fw = new FileWriter(f, true) ; } else {// 如果檔案不存在則建立新檔f.createNewFile() ; fw = new FileWriter(f) ; } fw.write(str + “\n”) ; // 將str字串變數資料寫入檔案並換行fw.close() ; // 結束檔案寫入動作} catch(Exception e) {}
檔案資料讀取-利用ScannerScanner sc = new Scanner(File filename) ; • 必須在try … catch區塊中使用 • 利用Scanner讀檔方式File f = new File(“filename.txt”) ;Scanner sc ;try { sc = new Scanner(f) ; while(sc.hasNext()) { System.out.println(sc.nextLine()) ; }} catch(Exception e) {} • while迴圈中的條件判斷會先確認文件中有出現字串資料後再讀取資料,如使用sc.hasNextLine()則表示先檢查是否有整列資料(以分行符號作為分隔) • 使用sc.nextLine()表示一次讀取整列資料,亦可使用sc.next(),表示一次讀一個字串 • 如果不是一般文字,則需用FileInputStream及FileOutputStream進行處理。
位元檔案串流處理- • 對於非文字的檔案存取,java.io套件的函式庫提供了處理位元組檔案串流的類別,FileInputStream與FileOutputStream,來處理讀取和寫入位元組或位元組陣列(參考java.io.InputStream及java.io.OutputStream),將檔案內容以二進位元逐一進行存取。物件宣告如下FileInputStream fis = new FileInputStream(filename) ;FileOutputStream fos = new FileOutputStream(filename) ; • filename: 代表輸入或輸出的檔案(型態為File)或檔名字串(型態為String) • 使用此物件時,必須在try…catch例外處理區塊中 • 存取資料時,要設定緩衝區暫存讀取的二進位位元組,再利用物件類別提供的read與write方法從緩衝區讀寫資料,直到所有資料讀完為止。 • 一般複製檔案時常用此種處理方式,備份如圖檔、影像等文件。
複製二進位元檔案(以Word檔為例)try { FileInputStream fis = new FileInputStream(new File("wordtest.doc" )); FileOutputStream fos = new FileOutputStream(new File("wordtest_bak.doc" )); int filesize = fis.available() ; byte[] buffer = new byte[1024] ; while(true) { if(fis.available() < 1024) { int remain ; do { remain = fis.read() ; fos.write(remain); } while(remain != -1) ; break ; } else { fis.read(buffer) ; fos.write(buffer) ; } } fis.close() ; fos.close() ;} catch (Exception e) {}
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.util.Date; import java.util.Scanner; public class Ex04_FileTest { public static void main(String[] args) { // File and directory property String filepath = "H:\\Course_Data\\CompLang\\JavaExample\\src\\" ; String filename = "testfile.txt" ; File f = new File(filename) ; if(f.exists()) { System.out.println(f.getName()) ; System.out.println(f.getPath()) ; System.out.println(f.getAbsolutePath()) ; } else { System.out.println("The file is not existing") ; } String[] filenames ; File fs = new File(filepath) ; 檔案存取範例
if(fs.isDirectory()) { filenames = fs.list() ; for(int i=0; i<filenames.length; i++) { File fname = new File(filenames[i]) ; String dir = "[" ; if(fname.isDirectory()) dir = dir + "d" ; else dir = dir + "-" ; if(fname.isFile()) dir = dir + "f" ; else dir = dir + "-" ; if(fname.canRead()) dir = dir + "r" ; else dir = dir + "-" ; if(fname.canWrite()) dir = dir + "w" ; else dir = dir + "-" ; if(fname.isHidden()) dir = dir + "h" ; else dir = dir + "-" ; dir = dir + "]" ; long time = fname.lastModified() ; Date date = new Date(time) ; System.out.print(dir + "\t" + filenames[i] + "\t" + fname.length() + " Bytes\t" + date + "\n") ; } } else { System.out.println("It is not a directory") ; }
// Read text file Scanner sc ; try { sc = new Scanner(f) ; while(sc.hasNext()) { System.out.println(sc.nextLine()) ; } } catch(Exception e) {} // Write text file File fcopy = new File(filepath + "testcopy.txt"); FileWriter fw ; try { if(fcopy.exists()) { fw = new FileWriter(fcopy,true) ; } else { fcopy.createNewFile() ; fw = new FileWriter(fcopy) ; } sc = new Scanner(f) ; while(sc.hasNext()) { fw.write(sc.nextLine()+"\n") ; } fw.close() ; } catch(Exception e) {}
// Rename file String newfname = filepath + "testnew.txt" ; fcopy.renameTo(new File(newfname)) ; // Delete file fcopy.delete() ; // Clone file String srcName = filepath + "wordtest.doc" ; String tarName = filepath + "wordtest_bak.doc" ; try { FileInputStream fis = new FileInputStream(new File(srcName)); FileOutputStream fos = new FileOutputStream(new File(tarName)); int filesize = fis.available() ; byte[] buffer = new byte[1024] ; System.out.println(filesize + " bytes are copying");
while(true) { if(fis.available() < 1024) { int remain ; do { remain = fis.read() ; fos.write(remain); } while(remain != -1) ; break ; } else { fis.read(buffer) ; fos.write(buffer) ; } System.out.println(fis.available() + " bytes are left"); } fis.close() ; fos.close() ; } catch (Exception e) {} } }