檔案讀寫與例外處理 (File IO and Exception Handling)

Slides:



Advertisements
Similar presentations
第一單元 建立java 程式.
Advertisements

鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
計算機程式語言實習課.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
四資二甲 第三週作業 物件導向程式設計.
C#程序设计案例教程 第3章 程 序 结 构.
第二章 JAVA语言基础.
File Access 井民全製作.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
C# 程式設計 第一部分 第1-4章 C# 程式設計 - 南華大學資管系.
Chapter 5 迴圈.
第5章 异常处理 王德俊 上海交通大学继续教育学院.
2.1 基本資料型別 2.2 變數 2.3 運算式與運算子 2.4 輸出與輸入資料 2.5 資料型別轉換 2.6 實例
程式設計概論 1.1 程式設計概論 程式語言的演進 物件導向程式 程式開發流程 1.2 C++開發工具
C#程序设计 c# programming 泛型 C#程序设计课程组.
第八章 C#高级编程.
.NET 程式設計入門(使用 C#) 講師:鄧智鴻.
第二章 C# 基础知识.
第四章 在 C# 中实现 OOP 概念.
Java基础 JavaSE异常.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
JAVA 程式設計與資料結構 第六章 輸出與輸入.
第十三章 文件和注册表操作.
西南科技大学网络教育系列课程 高级语程序设计(Java) 第五章 继承、接口与范型.
第三章 C#面向对象初级编程 面向对象得程序设计越来越受到编程人员的喜爱。类和对象是面向对象程序设计中的重要概念。封装性、继承性和多态性是面向对象的特点,本章旨在全面说明C#编写面向对象程序设计的方法。
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
類別(class) 類別class與物件object.
Java语言程序设计 第五部分 Java异常处理.
王豐緒 銘傳大學資訊工程學系 問題:JAVA 物件檔輸出入.
第三章 流程控制與例外處理 資訊教育研究室 製作 注意:本投影片僅供上課使用,非經同意,請勿散播或轉載。
C#面向对象程序设计 $7 继承和多态性.
文件流. 文件流 文件流 Stream FileStream BufferedStream MemoryStream NetworkStream …… 所有流的抽象 Stream FileStream BufferedStream MemoryStream NetworkStream ……
視窗程式設計 (Windows Programming)
例外處理與 物件序列化(Exception Handling and Serialization of Objects)
檔案讀寫與例外處理 (File IO and Exception Handling)
Java 程式設計 講師:FrankLin.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
service-side: throw Exceptions client-side: try-and-catch
第 十二 章 C# 中的文件处理.
第六章 属性、索引器、委托和事件.
P2P聊天工具.
類別與物件 I (Classes and Objects I)
第一單元 建立java 程式.
標籤、按鈕、工具列、狀態列 (Labels, Buttons, Tool Strips, and Status Strips)
第三章 C# 基础知识.
Week 2: 程式設計概念與 演算法的效能評估
Introduction to C Programming
一個基於Web Service的 洪氾預警系統
第7章 异常处理.
期末考.
第二章 Java基本语法 讲师:复凡.
File Input and Output Chap. 11: 施威銘的書 Chap. 7: K&R.
第二章 Java语法基础.
挑戰C++程式語言 ──第7章 輸入與輸出.
流程控制:Switch-Case 94學年度第一學期‧資訊教育 東海大學物理系.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
資料結構與C++程式設計進階 期末考 講師:林業峻 CSIE, NTU 7/ 15, 2010.
JAVA 程式設計與資料結構 第三章 物件的設計.
判斷(選擇性敘述) if if else else if 條件運算子.
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
第二章 Java基本语法 讲师:复凡.
Unix指令4-文字編輯與程式撰寫.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
Develop and Build Drives by Visual C++ IDE
方法(Method) 函數.
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
InputStreamReader Console Scanner
Presentation transcript:

檔案讀寫與例外處理 (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"); }