檔案讀寫與例外處理(ppt 檔, 427 KB)

Download Report

Transcript 檔案讀寫與例外處理(ppt 檔, 427 KB)

檔案讀寫與例外處理
(File IO and Exception
Handling)
鄭士康
國立台灣大學
電機工程學系/電信工程研究所/
資訊網路與多媒體研究所
1
綱要
1.
2.
3.
4.
5.
6.
7.
8.
簡易檔案讀寫
錯誤代碼
例外物件、try、catch、finally
自訂例外
例外類別的階層結構
巢狀try與catch
檔案類別
物件序列化
2
綱要
1.
2.
3.
4.
5.
6.
7.
8.
簡易檔案讀寫
錯誤代碼
例外物件、try、catch、finally
自訂例外
例外類別的階層結構
巢狀try與catch
檔案類別
物件序列化
3
加入文字檔
•
•
•
•
專案/加入新項目/文字檔/名稱/加入
鍵入資料
檔案/另存***為. . .
檢查儲存位置
4
串流(Stream)的觀念
輸入檔
StreamReader
Heap 記憶區
輸出檔
StreamWriter
5
FileDemo.Program (1/6)
/*
*
*
*
*
*
*
*
*
*
*
*
*
示範文字檔案之讀寫
skj 4/20/2007
假設欲讀入之資料檔格式如下:
第一列為一個整數代表以下每列有多少個整數數據
欲寫出之資料檔格式與欲讀入之資料檔格式相似
測試:
欲讀入之資料檔檔名為Test.dat
欲寫出之資料檔檔名為Test.output
6
FileDemo.Program (2/6)
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
欲讀入之資料檔內容為(數據之間以一個空白隔開)
3
1 4 2
2 3 5
7 6 9
8 3 12
11 10 235
寫出之資料檔內容相同, 但數據之間以TAB('\t')分開, 即
3
1
4
2
2
3
5
7
6
9
8
3
12
11
10
235
7
FileDemo.Program (3/6)
* 注意如果執行偵錯版本, 所用資料檔要放在專案的bin\Debug
* 檔案夾,
* 如果不偵錯, 須放在bin\Release檔案夾, 否則檔案名要用完整
* 檔案路徑
*/
using System;
using System.IO;
namespace FileDemo
{
class Program
{
static void Main(string[] args)
{
8
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);
9
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");
}
10
FileDemo.Program (6/6)
Console.WriteLine();
output.WriteLine();
}
output.Close();
input.Close();
}
}
}
11
練習
• 產生一文字資料檔,在其中用星號*和空白
製作圖案
• 撰寫程式讀取此一資料檔,並在螢幕印出
其中圖案
12
範例問題
•
假設有一N_ROW列、N_COL欄的整數表格,
寫一程式計算每列總和、每欄總和、及全部總
和。以如下2列3欄表格為例(Case 1):
3
4
2
5
7
6
其每列總和為 9、18,每欄總和為8、11、8,
全部總和為27。利用一個二維陣列儲存表格資
料,兩個一維陣列分別儲存每列總和及每欄總
和。
13
資料檔格式
2, 3
3, 4, 2
5, 7, 6
i.e.,
<nRow>, <nCol>
<data[0,0]>, <data[0,1]>, . . ., <data[0,nCol]>
...
<data[nRow,0]>, <data[nRow,1]>, . . ., <data[nRow, nCol]>
14
TableDemo.Program片段
using System;
using System.Diagnostics;
namespace TableDemo
{
class Program
{
static void Main(string[] args)
{
Debug.Assert(TableTest.Case1_OK(),
"Table test: Case 1 failed");
}
}
}
15
TableDemo.TableTest 片段
public static bool Case1_OK()
{
Table table = new Table("Test1.dat");
int[] rowSum = table.RowSum();
Debug.Assert(rowSum[0] == 9);
Debug.Assert(rowSum[1] == 18);
int[] colSum = table.ColSum();
Debug.Assert(colSum[0] == 8);
Debug.Assert(colSum[1] == 11);
Debug.Assert(colSum[2] == 8);
Debug.Assert(table.TotalSum() == 27);
return true;
}
16
TableDemo.Table 片段(1/6)
private int nRow;
private int nCol;
private int[,] data;
public Table(string fileName)
{
StreamReader input = new
StreamReader(fileName);
string line;
string[] head = new string[2];
line = input.ReadLine();
head = line.Split(',');
nRow = int.Parse(head[0]);
nCol = int.Parse(head[1]);
data = new int[nRow, nCol];
17
TableDemo.Table 片段(2/6)
string[] dataString = new string[nCol];
int i;
int j;
for (i = 0; i < nRow; ++i)
{
line = input.ReadLine();
dataString = line.Split(',');
for (j = 0; j < nCol; ++j)
{
data[i,j] = int.Parse(dataString[j]);
}
}
}
18
TableDemo.Table 片段(3/6)
public int[] RowSum()
{
int[] rowSum = new int[nRow];
for (int i = 0; i < nRow; ++i)
{
rowSum[i] = 0;
for (int j = 0; j < nCol; ++j)
{
rowSum[i] += data[i, j];
}
}
return rowSum;
19
TableDemo.Table 片段(4/6)
public int[] ColSum()
{
int[] colSum = new int[nCol];
for (int j = 0; j < nCol; ++j)
{
colSum[j] = 0;
for (int i = 0; i < nRow; ++i)
{
colSum[j] += data[i, j];
}
}
return colSum;
}
20
TableDemo.Table 片段(5/6)
public int TotalSum()
{
int totalSum = 0;
for (int i = 0; i < nRow; ++i)
{
for (int j = 0; j < nCol; ++j)
{
totalSum += data[i, j];
}
}
return totalSum;
}
21
練習
• 在類別Table中增加建構式public
Table(int[,] data) ,以便由一個矩
形陣列建立表格
• 在類別TableTest中增加public
static bool Case2_OK() ,以測試上
述建構式
• 修改主程式,進行測試
22
綱要
1.
2.
3.
4.
5.
6.
7.
8.
簡易檔案讀寫
錯誤代碼
例外物件、try、catch、finally
自訂例外
例外類別的階層結構
巢狀try與catch
檔案類別
物件序列化
23
錯誤代碼應用例虛擬碼(1/3)
readFile {
openFile;
determineSize;
allocateMemory;
readFileIntoMemory;
closeFile;
}
*吳瑞千譯, W. D. Mitchell原著,
JAVA 程式疑難排
解, 麥格羅希爾, 2001, pp. 3-8 ~3-10.
24
錯誤代碼應用例虛擬碼(2/3)
errorCodeType readFile {
initialize errorCode = 0;
openFile;
if( fileIsOpen ) {
determineSize;
if( gotFileLength ) {
allocateEnoughMemory;
if( memoryAllocated ) {
readFileIntoMemory;
if( readFailed ) {
errorCode = -1; handleError;
} else {
errorCode = -2; handleError;
}
25
錯誤代碼應用例虛擬碼(3/3)
} else {
errorCode = -3; handleError;
}
closeFile;
if( fileDidNotClose && errorCode == 0 ) {
errorCode = -4; handleError;
}
} else {
errorCode = -5; handleError;
}
return errorCode;
}
26
Try-Catch 應用例虛擬碼(1/2)
readFile {
try {
openFile;
determineSize;
allocateMemory;
readFileIntoMemory;
closeFile;
27
Try-Catch 應用例虛擬碼(2/2)
} catch( fileDidNotOpen ) {
handleError; // or throwException
} catch( sizeNotDetermined ) {
handleError; // or throwException
} catch( memoryAllocationFailed ) {
handleError; // or throwException
} catch( couldNotReadFile ) {
handleError; // or throwException
} catch( couldNotCloseFile ) {
handleError; // or throwException
}
28
例外處理優點
• 分離正常與錯誤處理程式碼,易於了解及
偵錯
• 例外物件包含所需清楚資訊,減輕記憶負
擔,易於使用
• 易於繼承衍生例外物件, 減少所需重複程
式碼
29
綱要
1.
2.
3.
4.
5.
6.
7.
8.
簡易檔案讀寫
錯誤代碼
例外物件、try、catch、finally
自訂例外
例外類別的階層結構
巢狀try與catch
檔案類別
物件序列化
30
UsingTryCatch.Program (1/3)
/*
* 示範try...catch之使用
* 5/14/2007
*/
using System;
namespace UsingTryCatch
{
class Program
{
static void Main(string[] args)
{
31
UsingTryCatch.Program (2/3)
try
{
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");
32
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");
}
}
}
}
33
UsingTryCatch2.Program (1/3)
/* 捕捉不同例外
* 5/14/2007
*/
using System;
namespace UsingTryCatch2
{
class Program
{
static void Main(string[] args)
{
bool success = false;
do
{
try
{
34
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("不可除以零");
Console.WriteLine(e.Message);
}
35
UsingTryCatch2.Program (3/3)
catch (Exception e)
{
Console.WriteLine("其他錯誤");
Console.WriteLine(e.Message);
}
finally
{
if (!success) Console.WriteLine("再試一次");
}
} while (!success);
}
}
}
36
練習
• 參考FileDemo程式,寫一程式打開一不存
在之資料檔,觀察會有何例外發生
• 修改程式catch此例外,印出訊息後結束
程式執行
37
綱要
1.
2.
3.
4.
5.
6.
7.
8.
簡易檔案讀寫
錯誤代碼
例外物件、try、catch、finally
自訂例外
例外類別的階層結構
巢狀try與catch
檔案類別
物件序列化
38
UserDefinedException.Program (1/5)
/*
* 示範使用者自訂例外之使用
* 5/22/2010
*/
using System;
namespace UserDefinedException
{
class Program
{
static void Main(string[] args)
{
int nStudents = 3;
39
UserDefinedException.Program (2/5)
Student[] table = new Student[nStudents];
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", 55);
Console.WriteLine("table[2] = {0} : {1}",
table[2].RegNo, table[2].Grade);
//Console.WriteLine("table[3] = {0} : {1}",
// table[3].RegNo, table[3].Grade);
}
40
UserDefinedException.Program (3/5)
catch (GradeOutOfRangeException e)
{
Console.WriteLine(e);
}
catch (IndexOutOfRangeException e)
{
Console.WriteLine(e.Message);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
41
UserDefinedException.Program (4/5)
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; }
}
public int Grade {
get { return grade; }
}
}
42
UserDefinedException.Program (5/5)
class GradeOutOfRangeException :
ApplicationException
{
private string message;
public GradeOutOfRangeException(
string regNo, int grade) : base()
{
message = "學生" + regNo + "之分數" + grade +
"不在0與100之間";
}
public override string ToString()
{
return message;
}
}
}
43
綱要
1.
2.
3.
4.
5.
6.
7.
8.
簡易檔案讀寫
錯誤代碼
例外物件、try、catch、finally
自訂例外
例外類別的階層結構
巢狀try與catch
檔案類別
物件序列化
44
例外類別階層
• Exception
– ApplicationException
– SystemException
• ArithmeticException
– DivideByZeroException
– NotFiniteNumberException
– OverflowException
• FormatException
• InvalidCastException
• IndexOutOfRangeException
• NullReferenceException
• StackOverflowException
• IOException
45
練習
• 撰寫類別Square,模擬邊長為a的正方形。
在以a為輸入參數之建構式中檢驗a是否為
正,若否則throw一自訂之例外物件
• 撰寫此一自定之例外類別
• 撰寫測試程式,嘗試建立邊長為-1的正方形,
並catch例外,輸出訊息後結束
46
綱要
1.
2.
3.
4.
5.
6.
7.
8.
簡易檔案讀寫
錯誤代碼
例外物件、try、catch、finally
自訂例外
例外類別的階層結構
巢狀try與catch
檔案類別
物件序列化
47
NestedTryAndCatch.Program(1/7)
/*
* 示範巢狀try...catch敘述之使用
* 5/22/2010
*/
using System;
namespace NestedTryAndCatch
{
class Program
{
static void Main(string[] args)
{
int nStudents = 1;
bool validNStudents = false;
48
NestedTryAndCatch.Program(2/7)
do
{
Console.Write("輸入學生人數: ");
try
{
nStudents = int.Parse(Console.ReadLine());
if (nStudents > 0) validNStudents = true;
}
catch (FormatException e)
{
Console.WriteLine(e.Message);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
} while (!validNStudents);
49
NestedTryAndCatch.Program(3/7)
try
{
Student[] table = new Student[nStudents];
for (int i = 0; i < nStudents; ++i)
{
bool validGrades = false;
do
{
Console.WriteLine("輸入第" + (i + 1) +
" 個學生學號及成績, 以一個空白分隔: ");
string line;
string[] data = new string[2];
try
{
line = Console.ReadLine();
data = line.Split(' ');
50
NestedTryAndCatch.Program(4/7)
string regNo = data[0];
int grade = Convert.ToInt32(data[1]);
table[i] = new Student(regNo, grade);
Console.WriteLine(
"table[" + i + "] = {0} : {1}",
table[i].RegNo, table[i].Grade);
validGrades = true;
}
catch (GradeOutOfRangeException e){
Console.WriteLine(e);
}
catch (FormatException e) {
Console.WriteLine(e.Message);
}
} while (!validGrades);
}
}
51
NestedTryAndCatch.Program(5/7)
catch (IndexOutOfRangeException e)
{
Console.WriteLine(e.Message);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
52
NestedTryAndCatch.Program(6/7)
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; }
}
public int Grade {
get { return grade; }
}
}
53
NestedTryAndCatch.Program(7/7)
class GradeOutOfRangeException :
ApplicationException
{
private string message;
public GradeOutOfRangeException(string regNo,
int grade)
: base()
{
message = "學生" + regNo + "之分數" + grade +
"不在0與100之間";
}
public override string ToString()
{
return message;
}
}
}
54
練習
• 將NestedTryAndCatch程式改為由資料
檔讀取資料,注意catch檔案不存在的例
外狀況
• 測試一錯誤資料檔,使程式提早遇到End
Of File的情況,觀察有何例外發生
• 修改程式,使能處理End Of File的情況
55
綱要
1.
2.
3.
4.
5.
6.
7.
8.
簡易檔案讀寫
錯誤代碼
例外物件、try、catch、finally
自訂例外
例外類別的階層結構
巢狀try與catch
檔案類別
物件序列化
56
串流(Stream)
檔案
位元組串流
程式變數
位元組串流
程式變數
Test.dat
網路接口
(Socket)
57
程式執行時自動產生的串流物件
• 類別Console的三個相關資料成員
• Console.In
– 連接至鍵盤,標準輸入串流物件
– 用於Console函式Read與ReadLine
• Console.Out
– 連接至螢幕,標準輸出串流物件
– 用於Console函式Write與WriteLine
• Console.Error
– 連接至螢幕,標準錯誤訊息輸出串流物件
58
命名空間System.IO
59
System.IO中的類別
• StreamReader
– 自檔案輸入文字
• StreamWriter
– 輸出文字至檔案
• FileStream
– 輸入及輸出至檔案均可能
• MemoryStream
• BufferedStream
60
綱要
1.
2.
3.
4.
5.
6.
7.
8.
簡易檔案讀寫
錯誤代碼
例外物件、try、catch、finally
自訂例外
例外類別的階層結構
巢狀try與catch
檔案類別
物件序列化
61
物件序列化(Serialization)與
去序列化(Deserialization)
• 利用BinaryFormatter,在Stream進行
• 序列化
– 將物件轉換為適當格式之資料,寫入檔案
• 去序列化
– 自檔案以適當格式讀出資料,重建物件
62
UsingSerialization.Program (1/8)
/*
* 以類別Student及GraduateStudent說明物件的序列化與
* 去序列化
* 5/22/2010
*/
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
namespace UsingSerialization
{
[Serializable]
class Student
{
private string regNo;
private string name;
63
UsingSerialization.Program (2/8)
public Student() {
regNo = "";
name = "";
}
public Student(string regNo, string name){
this.regNo = regNo;
this.name = name;
}
public string RegNO {
get { return regNo; }
}
public string Name
{
get { return name; }
}
}
64
UsingSerialization.Program (3/8)
[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;
}
public string Advisor {
get { return advisor; }
}
}
65
UsingSerialization.Program (4/8)
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 {
FileStream output = new
FileStream("Test.dat",
FileMode.Create, FileAccess.Write);
66
UsingSerialization.Program (5/8)
formatter.Serialize(output, so1);
formatter.Serialize(output, so2);
output.Close();
}
catch (SerializationException)
{
Console.WriteLine(
"Error writting to file in output");
}
catch (IOException)
{
Console.WriteLine(
"Can not create or close file for output");
}
try
{
67
UsingSerialization.Program (6/8)
FileStream input = new
FileStream("Test.dat",
FileMode.Open, FileAccess.Read);
try
{
while (true)
{
Object obj =
formatter.Deserialize(input);
if (
obj.GetType() == so1.GetType())
{
Student si =
(Student)obj;
68
UsingSerialization.Program (7/8)
Console.WriteLine(
si.RegNO + "\t" + si.Name );
}
else if (obj.GetType() ==
so2.GetType()) {
GraduateStudent si =
(GraduateStudent)obj;
Console.WriteLine(
si.RegNO + "\t" + si.Name +
"\t" + si.Advisor);
}
else {
throw new SerializationException();
}
}
}
69
UsingSerialization.Program (8/8)
catch (SerializationException) {
// close stream if no data left
input.Close();
}
catch (IOException) {
Console.WriteLine(
"Can not close file for input");
}
}
catch (FileNotFoundException) {
Console.WriteLine(
"Can not open file in building intput");
}
}
}
}
70