檔案讀寫與例外處理 (File IO and Exception Handling) 鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
綱要 簡易檔案讀寫 錯誤代碼及非物件導向的例外處理方式 例外物件、try、catch、throw、finally 例外類別的階層結構與自訂例外 巢狀try與catch 命名空間System.IO 物件序列化
綱要 簡易檔案讀寫 錯誤代碼及非物件導向的例外處理方式 例外物件、try、catch、throw、finally 例外類別的階層結構與自訂例外 巢狀try與catch 命名空間System.IO 物件序列化
加入文字檔 專案/加入新項目/文字檔/名稱/加入 鍵入資料 檔案/另存***為. . . 檢查儲存位置
FileOpeningClosing.Program.cs using System; using System.IO; namespace FileOpeningClosing { class Program static void Main(string[] args) Console.Write("輸入資料檔名: "); string fileName = Console.ReadLine(); StreamReader input = new StreamReader(fileName); input.Close(); input = new StreamReader(fileName); }
串流(Stream)的觀念 StreamReader 輸入檔 Heap 記憶區 StreamWriter 輸出檔
FileDemo.Program (1/6) /* * 示範文字檔案之讀寫 * skj 10/16/2010 * * 假設欲讀入之資料檔格式如下: * 第一列為一個整數代表以下每列有多少個整數數據 * 欲寫出之資料檔格式與欲讀入之資料檔格式相似 * 測試: * 欲讀入之資料檔檔名為Test.dat * 欲寫出之資料檔檔名為Test.output
FileDemo.Program (2/6) * 欲讀入之資料檔內容為(數據之間以一個空白隔開) * 3 * 1 4 2 * 2 3 5 * 7 6 9 * 8 3 12 * 11 10 235 * * 寫出之資料檔內容相同, 但數據之間以TAB('\t')分開, 即 * 1 4 2 * 2 3 5 * 7 6 9 * 8 3 12 * 11 10 235
FileDemo.Program (3/6) * 注意所用資料檔要放在專案執行檔所在檔案夾 */ using System; using System.IO; namespace FileDemo { class Program static void Main(string[] args)
FileDemo.Program (4/6) Console.Write("輸入欲讀入之資料檔名: "); string fileName = Console.ReadLine(); StreamReader input = new StreamReader(fileName); Console.Write("輸入欲寫出之資料檔名: "); fileName = Console.ReadLine(); StreamWriter output = new StreamWriter(fileName); string line; string[] head = new string[2]; // 讀入並寫出第一列 line = input.ReadLine(); int nDataPerLine = int.Parse(line); output.WriteLine(nDataPerLine);
FileDemo.Program (5/6) // 讀入並寫出後續數據 string[] dataString = new string[nDataPerLine]; int data; while (!input.EndOfStream) { line = input.ReadLine(); dataString = line.Split(' '); for ( int i = 0; i < nDataPerLine; ++i) data = int.Parse(dataString[i]); Console.Write(data + "\t"); output.Write(data + "\t"); }
FileDemo.Program (6/6) Console.WriteLine(); output.WriteLine(); } output.Close(); input.Close();
練習 產生一文字資料檔,在其中用星號*和空白製作圖案 撰寫程式讀取此一資料檔,並在螢幕印出其中圖案
範例問題: 學生成績 學號 學期成績 B645330 88 B645331 92 B645332 86
ScoreTableDemo程式說明 (1/3) /* * 以學生成績平均計算說明檔案處理 * 10/16/2010 * * 假設欲讀入之資料檔格式如下: * 第一列為兩個整數, 以一個空白分隔, * 分別代表學生數及科目數 * 第二列為各科名稱, 同樣以一個空白分隔 * 第三列起為學生資料, 每列第一項為學生學號, * 第二項起為依照第二列各科名稱順序排列之整數成績 * 各項之間以一個空白分隔
ScoreTableDemo程式說明 (2/3) * 資料讀完後, 算出各生總平均及各科全班平均 * 顯示所有成績及平均成績於螢幕, 平均成績顯示至小數第二位 * * 測試: * 欲讀入之資料檔檔名為Test.dat * 資料檔內容為(數據之間以一個空白隔開) * 3 2 * 計算機概論 計算機程式設計 * B645330 90 84 * B645331 88 86 * B645332 86 92 * 螢幕顯示(利用TAB鍵定位)
ScoreTableDemo程式說明 (3/3) * 學號/科目 計算機概論 計算機程式設計 個人平均成績 * B645330 90 84 87.00 * B645331 88 86 87.00 * B645332 86 92 89.00 * 全班平均成績 88.00 87.33 * * 注意所用資料檔要放在專案的bin\Debug檔案夾 */
程式ScoreTableDemo的UML類別圖
ScoreTableDemo.Program片段(1/4) using System; using System.IO; namespace ScoreTableDemo { class Program { static void Main(string[] args) { Console.Write("輸入欲讀入之資料檔名: "); string fileName = Console.ReadLine(); ScoreTable table = new ScoreTable(fileName); DisplayTable(table); }
ScoreTableDemo.Program片段(2/4) static void DisplayTable(ScoreTable table) { // 顯示表頭 Console.Write("學號/科目\t"); string[] subject = table.Subject; int nSubjects = subject.Length; int i; for (i = 0; i < nSubjects; ++i) Console.Write(subject[i]+"\t"); } Console.Write("個人平均成績"); Console.WriteLine();
ScoreTableDemo.Program片段(3/4) // 顯示學號, 成績, 個人平均 string[] registerNumber = table.RegisterNumber; int nStudents = registerNumber.Length; int[,] score = table.Score; double[] individualAverage = table.IndividualAverage(); int n; for (n = 0; n < nStudents; ++n) { Console.Write(registerNumber[n] + "\t\t"); for (i = 0; i < nSubjects; ++i) { Console.Write("{0}\t\t", score[n,i]); }
ScoreTableDemo.Program片段(4/4) Console.Write("{0:F2}", individualAverage[n]); Console.WriteLine(); } // 顯示各科之全班平均 double[] subjectAverage = table.SubjectAverage(); Console.Write("全班平均成績\t"); for (i = 0; i < nSubjects; ++i) { Console.Write("{0:F2} \t\t", subjectAverage[i]);
ScoreTableDemo.ScoreTable (1/6) class ScoreTable { private int[,] score; private string[] subject; private string[] registerNumber; private int nStudents; private int nSubjects; // 藉由讀取檔案資料建立物件 public ScoreTable(string fileName) StreamReader input = new StreamReader(fileName);
ScoreTableDemo.ScoreTable (2/6) string line; // 讀入學生數及科目數 line = input.ReadLine(); string[] head = new string[2]; head = line.Split(' '); nStudents = int.Parse(head[0]); nSubjects = int.Parse(head[1]); registerNumber = new string[nStudents]; score = new int[nStudents, nSubjects]; // 讀入各科名稱 subject = new string[nSubjects]; subject = line.Split(' ');
ScoreTableDemo.ScoreTable (3/6) // 讀入各生學號及各科成績 int n; int i; string[] dataString = new string[nSubjects + 1]; for (n = 0; n < nStudents; ++n) { line = input.ReadLine(); dataString = line.Split(' '); registerNumber[n] = dataString[0]; for (i = 0; i < nSubjects; ++i) score[n, i] = int.Parse(dataString[i + 1]); }
ScoreTableDemo.ScoreTable (4/6) public string[] Subject { get { return subject; } } public string[] RegisterNumber get { return registerNumber; } public int[,] Score get { return score; }
ScoreTableDemo.ScoreTable (5/6) public double[] IndividualAverage() { double[] average = new double[nStudents]; int n; int i; double sum; for (n = 0; n < nStudents; ++n) sum = 0.0; for (i = 0; i < nSubjects; ++i) sum += score[n,i]; } average[n] = sum / nSubjects; return average;
ScoreTableDemo.ScoreTable (6/6) public double[] SubjectAverage() { double[] average = new double[nSubjects]; int i; int n; double sum; for (i = 0; i < nSubjects; ++i) { sum = 0.0; for (n = 0; n < nStudents; ++n) { sum += score[n,i]; } average[i] = sum / nStudents; return average;
綱要 簡易檔案讀寫 錯誤代碼及非物件導向的例外處理方式 例外物件、try、catch、throw、finally 例外類別的階層結構與自訂例外 巢狀try與catch 命名空間System.IO 物件序列化
讀取檔案之虛擬碼 1. 開啟檔案; 2. 決定檔案大小; 3. 配置緩衝器記憶; 4. 讀取檔案內容到緩衝器; 5. 關閉檔案;
以錯誤代碼處理例外之虛擬碼 (1/3) 配置緩衝器記憶; if(緩衝器記憶配置妥當) errCode = 0; // 錯誤代碼 開啟檔案; { 決定檔案大小; if(檔案大小可以決定) 配置緩衝器記憶; if(緩衝器記憶配置妥當) 讀取檔案內容到緩衝器; if(檔案內容讀取正常結束) 關閉檔案;
以錯誤代碼處理例外之虛擬碼 (2/3) if(檔案關閉有問題) { errCode = 5; 傳回或顯示errCode後離開本程式; } else errCode = 4;
以錯誤代碼處理例外之虛擬碼 (3/3) errCode = 2; else { errCode = 3; } errCode = 2; errCode = 1;
錯誤代碼表 錯誤代禡 錯誤說明 正常情況,沒有錯誤或例外發生 1 檔案無法開啟 2 檔案大小無法決定 3 無法配置記憶給緩衝器 4 正常情況,沒有錯誤或例外發生 1 檔案無法開啟 2 檔案大小無法決定 3 無法配置記憶給緩衝器 4 檔案內容無法讀取或讀取發生問題 5 檔案無法關閉
另一種利用錯誤代碼的方法 (1/2) errCode = 0 開啟檔案; if(檔案無法開啟) { errCode = 1; } 決定檔案大小; if(檔案大小無法決定) errCode = 2;
另一種利用錯誤代碼的方法 (2/2) 配置緩衝器記憶; if(無法配置記憶給緩衝器) { errCode = 3; } 讀取檔案內容到緩衝器; if(檔案內容無法讀取或讀取發生問題) { errCode = 4; 關閉檔案; if(檔案無法關閉) { errCode = 5;
主要缺失 正常流程被例外處理流程中斷,使程式的正常流程不容易看得出來,不容易閱讀與追蹤偵錯 如果例外處理的程式碼相當長或複雜,在整個程式中追蹤正常流程可能會有困難
綱要 簡易檔案讀寫 錯誤代碼及非物件導向的例外處理方式 例外物件、try、catch、throw、finally 例外類別的階層結構與自訂例外 巢狀try與catch 命名空間System.IO 物件序列化
Try-Catch 應用例虛擬碼(1/2) try { 開啟檔案; 決定檔案大小; 配置緩衝器記憶; 讀取檔案內容到緩衝器; 關閉檔案 } catch( FileNotOpened e) 處理例外或再丟出例外物件; catch( SizeNotDetermined e)
Try-Catch 應用例虛擬碼(2/2) catch( MemoryNotAllocated e) { 處理例外或再丟出例外物件; } catch( ReadingNotCompleted e) catch( FileNotClosed e )
例外處理優點 分離正常與錯誤處理程式碼,易於了解及偵錯 例外物件包含所需清楚資訊,減輕記憶負擔,易於使用 易於繼承衍生例外物件, 減少所需重複程式碼
UsingTryCatch0.Program /* * 示範try...catch之使用 * 不處理例外之原始版本 * 10/26/2010 */ . . . . . . class Program { static void Main(string[] args) Console.Write("輸入除數: "); // 輸入0以產生例外 int number = int.Parse(Console.ReadLine()); int result = 5 / number; }
UsingTryCatch.Program (1/3) /* * 示範try...catch之使用 * 5/14/2007 */ using System; namespace UsingTryCatch { class Program static void Main(string[] args)
UsingTryCatch.Program (2/3) { Console.Write("輸入除數: "); int number = int.Parse(Console.ReadLine()); int result = 5 / number; } catch (Exception e) Console.WriteLine("發生例外"); Console.WriteLine("例外原因: " + "\n\n" + e.Message + "\n"); Console.WriteLine("擲出例外之方法的呼叫堆疊: " + "\n\n" + e.StackTrace + "\n");
UsingTryCatch.Program (3/3) Console.WriteLine("擲出例外之地點: " + "\n\n" + e.TargetSite + "\n"); Console.WriteLine( "擲出例外之程式物件來源: " + "\n\n" + e.Source + "\n"); Console.WriteLine("例外之相關細節: " + "\n\n" + e.ToString() + "\n"); }
UsingTryCatch2.Program (1/3) /* 捕捉不同例外 * 10/30/2010 */ using System; namespace UsingTryCatch2 { class Program static void Main(string[] args) bool success = false; do try
練習 參考FileDemo程式,寫一程式打開一不存在之資料檔,觀察會有何例外發生 修改程式catch此例外,印出訊息後結束程式執行
UsingTryCatch2.Program (2/3) Console.Write("輸入除數: "); int number = int.Parse(Console.ReadLine()); int result = 5 / number; Console.WriteLine("除結果為" + result); success = true; } catch (FormatException e) { Console.WriteLine("請輸入數值"); Console.WriteLine(e.Message); catch (DivideByZeroException e) Console.WriteLine("不可除以零");
UsingTryCatch2.Program (3/3) catch (Exception e) { Console.WriteLine("其他錯誤"); Console.WriteLine(e.Message); } if (!success) Console.WriteLine("再試一次"); } while (!success);
UsingThrow.Program (1/4) /* * 示範使用throw拋出例外物件 * 11/5/2010 */ using System; using System.IO; namespace UsingThrow { class Program static void Main(string[] args) Console.Write("輸入欲開啟之檔名:"); string fileName = Console.ReadLine();
UsingThrow.Program (2/4) try { int answer = Test(fileName); } catch (Exception e) Console.WriteLine( "主程式捕捉到例外:" + e.Message); static int Test(string fileName)
UsingThrow.Program (3/4) try { StreamReader input = new StreamReader(fileName); int number = int.Parse(input.ReadLine()); int result = 5 / number; input.Close(); return result; } catch (IOException e) Console.WriteLine( "函式Test捕捉到IO例外:" + e.Message); Console.WriteLine();
UsingThrow.Program (4/4) Console.WriteLine( "函式Test再拋出例外:" + e.Message); Console.WriteLine(); throw e; } catch (Exception e) { "函式Test捕捉到例外:" + e.Message);
UsingFinally.Program (1/4) /* * 示範finally區塊的使用 * 11/6/2010 */ using System; using System.IO; namespace UsingFinally { class Program static void Main(string[] args) Console.Write("輸入欲開啟之檔名:"); string fileName = Console.ReadLine(); int answer = 0;
UsingFinally.Program (2/4) try { answer = Test(fileName); } catch (Exception e) Console.WriteLine( "主程式捕捉到例外:" + e.Message); Console.WriteLine("answer = " + answer); static int Test(string fileName) StreamReader input = new StreamReader(fileName); int result = 0;
UsingFinally.Program (3/4) try { int number = int.Parse(input.ReadLine()); result = 5 / number; } catch (Exception e) Console.WriteLine( "函式Test捕捉到例外:" + e.Message); "函式Test再拋出例外:" + e.Message); throw e;
UsingFinally.Program (4/4) { Console.WriteLine( "函式Test進入finally區塊"); input.Close(); "函式Test完成關閉檔案"); "函式Test離開finally區塊"); } return result;
綱要 簡易檔案讀寫 錯誤代碼及非物件導向的例外處理方式 例外物件、try、catch、throw、finally 例外類別的階層結構與自訂例外 巢狀try與catch 命名空間System.IO 物件序列化
例外類別繼承關係
UsingProgrammerDefinedException 類別圖
UsingProgrammerDefinedException.Program (1/3) /* * 示範程式師自訂例外之使用 * 11/13/2010 */ using System; namespace UsingProgrammerDefinedException { class Program static void Main(string[] args) int nStudents = 4; Student[] table = new Student[nStudents];
UsingProgrammerDefinedException.Program (2/3) try { table[0] = new Student("B645330", 90); Console.WriteLine("table[0] = {0} : {1}", table[0].RegNo, table[0].Grade); // table[1] = new Student("B645331", 102); table[1] = new Student("B645331", 100); Console.WriteLine("table[1] = {0} : {1}", table[1].RegNo, table[1].Grade); table[2] = new Student("B645332", 85); Console.WriteLine("table[2] = {0} : {1}", table[2].RegNo, table[2].Grade);
UsingProgrammerDefinedException.Program (3/3) table[3] = new Student("B645332", 75); Console.WriteLine("table[3] = {0} : {1}", table[3].RegNo, table[3].Grade); } catch (GradeOutOfRangeException e) { Console.WriteLine(e); catch (IndexOutOfRangeException e) { Console.WriteLine(e.Message); catch (Exception e) {
UsingProgrammerDefinedException.Student (1/2) class Student { private string regNo; private int grade; public Student(string regNo, int grade) if (grade < 0 || grade > 100) throw new GradeOutOfRangeException( regNo, grade); this.regNo = regNo; this.grade = grade; } public string RegNo get { return regNo; }
UsingProgrammerDefinedException.Student (2/2) public int Grade { get { return grade; } }
UsingProgrammerDefinedException.GradeOutOfRangeException class GradeOutOfRangeException : ApplicationException { private string message; public GradeOutOfRangeException( string regNo, int grade) : base() message = "學生" + regNo + "之分數" + grade + "不在0與100之間"; } public override string ToString() return message;
練習 撰寫類別Square,模擬邊長為a的正方形。在以a為輸入參數之建構式中檢驗a是否為正,若否則throw一自訂之例外物件 撰寫此一自定之例外類別 撰寫測試程式,嘗試建立邊長為-1的正方形,並catch例外,輸出訊息後結束
綱要 簡易檔案讀寫 錯誤代碼及非物件導向的例外處理方式 例外物件、try、catch、throw、finally 例外類別的階層結構與自訂例外 巢狀try與catch 命名空間System.IO 物件序列化
NestedTryCatch0.Program(1/4) /* * 示範nested try-catch的使用 * 11/13/2010 */ using System; using System.IO; namespace NestedTryCatch0 { class Program static void Main(string[] args) Console.Write("輸入欲開啟之檔名:");
NestedTryCatch0.Program(2/4) string fileName = Console.ReadLine(); int answer = Test(fileName); Console.WriteLine("answer = " + answer); } static int Test(string fileName) { int result = 0; try StreamReader input = new StreamReader(fileName);
NestedTryCatch0.Program(3/4) int number = int.Parse(input.ReadLine()); result = 5 / number; } catch (Exception e) { Console.WriteLine( "函式Test內層try-catch捕捉到例外:" + e.Message); "函式Test內層try-catch再拋出例外:" throw e;
NestedTryCatch0.Program(4/4) finally { Console.WriteLine( "函式Test內層try-catch進入finally區塊"); input.Close(); "函式Test內層try-catch完成關閉檔案"); "函式Test內層try-catch離開finally區塊"); } catch (Exception e) { "函式Test外層try-catch捕捉到例外:" + e.Message); return result; } } }
建檔程式 寫一個程式,由鍵盤逐筆輸入資料,並依照先前處理學生成績程式要求之格式,寫入檔案,使完成的檔案可以被先前程式處理,這種過程通常稱為建檔 雖然我們也可以利用文書處理器產生文字檔的方法建檔,但是所需資料檔的格式可能會很複雜,建檔者很容易因記不清楚,產生不合規格的資料檔;因此一個依格式要求寫成的建檔程式,比較能確保資料檔格式合乎要求
建檔程式 建檔時可能有很多筆資料要鍵入,程式要能在適當時機回應顯示(echo)已輸入資料,讓使用者檢查更正或確認無誤(confirm),避免使用者已經耗費時間鍵入許多資料後,發生錯誤,使他又要重頭開始,這會讓使用者產生極大的挫折感
建檔程式正常流程虛擬碼 建立串流寫出器 決定並寫出學生人數及科目數 輸入並寫出各科目名稱 輸入並寫出各生學號與各科成績 關閉串流寫出器
考慮echo之建檔程式正常流程虛擬碼 dataConfirmed = false; do { 輸入學生數及科目數; echo學生數及科目數並要求確認; if(確認無誤) dataConfirmed = true; } while (!dataConfirmed); 將學生數及科目數經由串流寫出器寫出至檔案;
考慮echo及例外之建檔程式虛擬碼 (1/2) 1. dataConfirmed = false; 2. do { 2.1 validNStudents = false; 2.2 validNSubjects = false; try 2.3 輸入學生數及科目數; 2.4 if(學生數>0) validNStudents = true; 2.5 if(科目數>0) validNSubjects = true; 2.6 echo學生數及科目數並要求確認; 2.7 if(確認無誤) dataConfirmed = true; }
考慮echo及例外之建檔程式虛擬碼 (2/2) catch(FormatException e) { 2.8 處理輸入格式錯誤例外; } catch(Exception e) 2.9 處理其他例外; } while(!validNStudents 或 !validNSubjects 或 !dataConfirmed); 3. 將學生數及科目數經由串流寫出器寫出至檔案;
NestedTryAndCatch.Program(1/12) /* * 以成績建檔示範巢狀try...catch敘述 * 及迴圈之混合使用 * 1/15/2011 */ using System; using System.IO; namespace NestedTryAndCatch { class Program static void Main(string[] args)
NestedTryAndCatch.Program(2/12) string fileName = ""; int nStudents = 1; int nSubjects = 1; bool validNStudents; bool validNSubjects; string answer = "Y"; bool dataConfirmed = false; try { // 建立串流寫出器 Console.Write("請指定輸出檔名: "); fileName = Console.ReadLine(); StreamWriter output = new StreamWriter(fileName);
NestedTryAndCatch.Program(3/12) // 決定學生人數及科目數 do { validNStudents = false; validNSubjects = false; try // prompt and get input Console.Write("輸入學生人數: "); nStudents = int.Parse(Console.ReadLine()); if (nStudents > 0) validNStudents = true; Console.Write("輸入科目數: "); nSubjects =
NestedTryAndCatch.Program(4/12) if (nSubjects > 0) validNSubjects = true; // echo and confirm Console.WriteLine( "學生: " + nStudents + "人; " + "科目: " + nSubjects + "科. " + "正確請按Y或y,錯誤請按N或n"); answer = Console.ReadLine(); dataConfirmed = answer != "N" && answer != "n"; } catch (FormatException e) { Console.WriteLine(e.Message);
NestedTryAndCatch.Program(5/12) catch (Exception e) { Console.WriteLine(e.Message); } } while (!validNStudents || !validNSubjects || !dataConfirmed); output.WriteLine("{0} {1}", nStudents, nSubjects); // 輸入並寫出科目名稱 string[] subject = new string[nSubjects]; for (int i = 0; i < nSubjects; ++i)
NestedTryAndCatch.Program(6/12) do { // prompt and get input Console.Write( "輸入第" + (i + 1) + "科科目名稱: "); subject[i] = Console.ReadLine(); // echo and confirm Console.WriteLine( "第" + (i + 1) + "科科目名稱: " + subject[i] + "; 正確請按Y或y,錯誤請按N或n"); answer = Console.ReadLine(); dataConfirmed = answer != "N" && answer != "n"; } while (!dataConfirmed);
NestedTryAndCatch.Program(7/12) output.Write(subject[i]); if (i < nSubjects - 1) output.Write(" "); } output.WriteLine(); // 輸入並寫出各生學號及各科成績 string line; string[] data = new string[nSubjects]; int[] grade = new int[nSubjects]; for (int n = 0; n < nStudents; ++n) { string registerNumber; bool validGrades = false;
NestedTryAndCatch.Program(8/12) do { Console.Write( "輸入第" + (n + 1) + "個學生學號: "); registerNumber = Console.ReadLine(); Console.WriteLine("輸入" + registerNumber + "各科成績, 以空白分隔: "); line = Console.ReadLine(); data = line.Split(' '); for (int i = 0; i < nSubjects; ++i) try
NestedTryAndCatch.Program(9/12) grade[i] = Convert.ToInt32(data[i]); if (grade[i] < 0 || grade[i] > 100) throw new GradeOutOfRangeException( registerNumber, grade[i]); validGrades = true; } catch (GradeOutOfRangeException e) { Console.WriteLine(e);
NestedTryAndCatch.Program(10/12) catch (FormatException e) { Console.WriteLine(e.Message); } Console.WriteLine( "學號: " + registerNumber); for(int i=0; i<nSubjects; ++i) { "科目:" + subject[i] + "成績:" + grade[i]);
NestedTryAndCatch.Program(11/12) // echo and confirm Console.WriteLine( "資料正確請按Y或y,錯誤請按N或n"); answer = Console.ReadLine(); dataConfirmed = answer != "N" && answer != "n"; } while (!validGrades || !dataConfirmed); output.Write(registerNumber + ' '); for (int i = 0; i < nSubjects; ++i) { output.Write(grade[i]); if(i < nSubjects-1) output.Write(" "); } output.WriteLine();
NestedTryAndCatch.Program(12/12) output.Close(); Console.WriteLine("資料已寫入" + fileName); } catch (Exception e) { Console.WriteLine(e.Message);
NestedTryAndCatch. GradeOutOfRangeException class GradeOutOfRangeException :ApplicationException { private string message; public GradeOutOfRangeException(string regNo, int grade) : base() message = "學生" + regNo + "之分數" + grade + "不在0與100之間"; } public override string ToString() return message;
綱要 簡易檔案讀寫 錯誤代碼及非物件導向的例外處理方式 例外物件、try、catch、throw、finally 例外類別的階層結構與自訂例外 巢狀try與catch 命名空間System.IO 物件序列化
命名空間System.IO
串流(Stream) 檔案 程式變數 位元組串流 Test.dat 程式變數 位元組串流 網路接口 (Socket)
程式執行時自動產生的串流物件 類別Console的三個相關資料成員 Console.in Console.out Console.error 連接至鍵盤,標準輸入串流物件 用於Console函式Read與ReadLine Console.out 連接至螢幕,標準輸出串流物件 用於Console函式Write與WriteLine Console.error 連接至螢幕,標準錯誤訊息輸出串流物件
System.IO中的類別 StreamReader StreamWriter FileStream MemoryStream 自檔案輸入文字 StreamWriter 輸出文字至檔案 FileStream 輸入及輸出至檔案均可能 MemoryStream BufferedStream
綱要 簡易檔案讀寫 錯誤代碼及非物件導向的例外處理方式 例外物件、try、catch、throw、finally 例外類別的階層結構與自訂例外 巢狀try與catch 命名空間System.IO 物件序列化
物件序列化(Serialization)與 去序列化(Deserialization) 利用BinaryFormatter,在Stream進行 序列化 將物件轉換為適當格式之資料,寫入檔案 去序列化 自檔案以適當格式讀出資料,重建物件 如果類別的成員變數包含其他物件,或落在某個複雜繼承體系的底層,想把對應物件的所有成員變數都井然有序地寫入檔案,再絲毫不亂地讀出來,據以建立相同內容的物件,是相當繁瑣困難的工作
物件序列化與去序列化一例
UsingSerialization.Student (1/2) [Serializable] class Student { private string regNo; private string name; public Student() regNo = ""; name = ""; } public Student(string regNo, string name) this.regNo = regNo; this.name = name;
UsingSerialization.Student (2/2) public string RegNO { get { return regNo; } } public string Name get { return name; }
UsingSerialization.GraduateStudent (1/2) [Serializable] class GraduateStudent : Student { private string advisor; public GraduateStudent() : base() advisor = ""; } public GraduateStudent( string regNo, string name, string advisor) : base(regNo, name) this.advisor = advisor;
UsingSerialization.GraduateStudent (2/2) public string Advisor { get { return advisor; } }
UsingSerialization.Program (1/7) /* * 以類別Student及GraduateStudent說明物件的序列化與去序列化 * 1/21/2011 */ using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Diagnostics; namespace UsingSerialization {
UsingSerialization.Program (2/7) class Program { static void Main(string[] args) Student so1 = new Student( "B645331", "Thomas"); GraduateStudent so2 = new GraduateStudent( "F685329", "Richard", "Aaron"); BinaryFormatter formatter = new BinaryFormatter(); try
UsingSerialization.Program (3/7) FileStream output = new FileStream( "Test.dat", FileMode.Create, FileAccess.Write); formatter.Serialize(output, so1); formatter.Serialize(output, so2); output.Close(); } catch (SerializationException) { Console.WriteLine( "Error writting to file in output"); catch (IOException) "Can not create or close file for output");
UsingSerialization.Program (4/7) try { FileStream input = new FileStream( "Test.dat", FileMode.Open, FileAccess.Read); while (input.CanRead) Object obj = formatter.Deserialize(input); if (obj.GetType() == so1.GetType())
UsingSerialization.Program (5/7) Student si = (Student)obj; Debug.Assert( si.RegNO == so1.RegNO && si.Name == so1.Name); } else if (obj.GetType() == so2.GetType()) { GraduateStudent si = (GraduateStudent)obj; si.RegNO == so2.RegNO && si.Name == so2.Name && si.Advisor == so2.Advisor);
UsingSerialization.Program (6/7) else { throw new SerializationException(); } catch (SerializationException) { // close stream if no data left input.Close(); catch (IOException) { Console.WriteLine( "Can not close file for input");
UsingSerialization.Program (7/7) catch (FileNotFoundException) { Console.WriteLine( "Can not open file in building intput"); }