第7章 C#中的文件,数组和异常
C#处理数组和文件相当容易,并引入异常来简化错误处理。
7.1 数组
const int MAX=10;
float[] xy=new float[MAX];
for (int i=0;i<MAX;i++){
xy[i]=i;
}
应当习惯数组的循环是从零到数组边界值减1,正如我们在前面的代码中所做的那样。
所有的数组变量都有一个长度属性,可以根据它查出数组的大小。
float[] z=new float[20];
for (int j=0;j<=z.length;j++){
z[j]=j;
}
C#中的数组是动态的,可以在任何时候重新分配空间。
在类内部创建一个数组的引用,然后分配空间,可这样写:
float[] z; //declare here
z=new float[20]; //create later
7.2 集合对象
7.2.1 ArrayList
ArrayList(数组表)对象本质上是一个可变长的数组,可以根据需要添加元素
使用ArrayList的方法可以向数组中添加元素,或取出,修改某个元素.
float[] z={1.0f,2.9f,5.6f};
ArrayList arl=new ArrayList();
for (int j=0;j<z.length;j++){
arl.Add(z[j]);
}
ArrayList有一个Count属性,可用于确定它所包含的元素数目.
for (j=0;j<arl.count;j++){
Console.WriteLine(arl[j]);
}
foreach(float a in arl){
Console.WriteLine(a);
}
ArrayList的方法列表:
Clear
删除ArrayList的内容
Contains(object)
如果ArrayList包含该对象,则返回true
CopyTo(array)
将ArrayList全部内容拷贝到一个一维数组中
IndexOf(object)
返回该对象的第一个索引值
Insert(index,object)
将元素插入到指定的位置
Remove(object)
从表中删除该元素
RemoveAt(index)
删除指定对象的元素
Sort
对ArrayList排序
从ArrayList中取出的每个对象都是object类型,这就意味着,在使用前通常需要将其转换成合适的类型.
float x=(float)arl[j];
7.2.2 Hashtable 哈希表
Hashtable是一个可变长数组,表中的每个项目都通过关键字值来访问.
关键字一般是某个字符串,也可以是其他类型的对象.
使用Hashtable可以快速访问一个大而无序的记录表
还可以将关键字和项目值颠倒过来,创建一个每条项目都是唯一的表.
Hashtable hash=new Hashtable();
float freddy=12.3f;
hash.Add("fred",freddy); //add to table
//get this one back out
float temp=(float)hash["fred"];
注意,同ArrayList一样.从Hashtable中得到的数据也必须转换成合适的类型
Hashtable也有一个Count属性,可用来获取关键字的列表或项目的列表.
7.2.3 SortedList
SortedList(排序表)类保存了两个内部数组,可以通过基于零的下标或字母关键字来获取元素.
float sammy=44.55f;
SortedList slist=new SortedLIst();
slist.Add("fred",freddy);
slist.Add("sam",sammy);
//get by index
float newFred=(float)slist.GetByIndex(0);
//get by key
float newSam=(float)slist["sam"];
在System.Collections名字空间还可以看到Stack(堆栈)对象和Queue(队列)对象,
他们的特性和读者期望一样,可以在系统帮助文档中找到他们的方法.
7.3 异常
C#用异常完成错误处理,而不是采用其他难以使用的错误检测方法。
异常处理就是把引起错误的语句包含在try块中,然后用catch语句捕获错误.
try{
//Statements
}
catch(Exception e){
//do these if an error occurs
finally{
//do these anyway
}
一般使用这种方法检查文件处理语句的错误,
也可以用语捕获数组下标越界和许多其他错误情形。
执行try块中的语句若无错误,则转到finally语句;
若有错误,则转到catch语句,然后转到finally语句.
try{
//note-one too many
for(int i=0;i<=arl.count(arl[i]);
}
catch(Exception e){
Console.WriteLine(e.Message);
}
这段程序输出了错误信息和程序中的调用位置,然后继续运行.
相反,若不捕获异常,运行时会给出一条错误信息.
程序将退出运行不再继续下去并给出出错界面.
C#中的常用异常类
AccessException
访问类的方法或数据时出错
ArgumentException
传给方法的参数是无效的
ArgumentNullException
参数是空的
ArithmeticException
上溢或下溢
DivideByZereException
被0除
IndexOutOfRangeException
文件没找到
EndOfStreamException
访问超出了输入数据流(如文件)的末端
DirectoryNotFoundException
目录没找到
NullReferenceException
对象变量没有初始化成一个实际的值
7.4 多个异常
也可以在一系列catch块中捕获多个异常并分别进行处理:
try{
for(int i=0;i<=arl.count;i++){
int k=(int)(float)arl[i];
Console.Write(i+""+k/i);
}
}
catch(DivideByZereException e){
printZero(e);
}
catch(IndexOutofRangeException e){
printOErr(e);
}
catch(Exception e){
printErr(e);
}
这样做可以让程序以不同方式从各种错误中恢复过来.
7.5 抛出异常
try{
//statements
}
catch(Exception e){
throw(e); //pass on to calling program
}
7.6 文件处理
C#中的文件处理对象提供了一些相当灵活的处理文件的方法.
7.6.1 File对象
File(文件)对象代表一个文件,并为检测文件是否存在,文件更名和文件删除提供了一些有用的方法。
所有这些方法都是静态的,也就是说,
不能使用new运算符创建file的实例,但可以直接使用File的方法.
if(File.Exists("Foo.txt"))
File.Delete("foo.txt");
用File对象获得用于读写文件数据的文件流(FileStream)
//open text file for reading
StreamReader ts=File.OpenText("foo1.txt");
//open any type of file for reading
FileStream fs=File.OpenRead("foo2.any");
一些比较有用的File方法:
File.FileExists(filename)
若文件存在,则为true
File.Delete(filename)
删除该文件
File.AppendText(String)
添加文本
File.Copy(fromFile,toFile)
拷贝一个文件
File.Move(fromFile,toFile)
移动文件,删除旧文件
File.GetExtension(filename)
返回文件的扩展名
File.HasExtension(filename)
若文件有扩展名,则为true
7.6.2 读文本文件
StreamReader ts=File.OpenText("foo1.txt");
string s=ts.ReadLine();
7.6.3 写文本文件
//open for writing
StreamWriter sw=File.CreateText("foo3.txt");
sw.WriteLine("Hello file");
7.6.3 写文本文件
//append to text file
StreamWriter asw=new StreamWriter("foo1.txt",true);
//true为用于追加的布尔参数
7.7 文件处理中的异常
try{
//open text file for reading
StreamReader ts=File.OpenText("foo1.txt");
string s=ts.ReadLine();
}
catch(Exception e){
Console.WriteLine(e.Message);
}
7.8 检测文件末尾
private StreamReader rf;
private bool eof;
public String readLine(){
String s=rf.ReadLine();
if(s==null)
eof=true;
return s;
}
public bool fEof(){ //文件读入类中创建一个文件结束函数
return eof;
}
另外一种保证读数据不会超过文件末尾的方法:
Peek方法返回文件中下一个字符的Ascii码,如果没有字符则返回-1.
public String read_Line(){
String s="";
if(rf.Peek()>0){
s=rf.ReadLine();
}
else{
eof = true;
}
return s;
}
7.9 csFile类
将文件方法包装在一个带有易用方法的简单类中会更方便,于是我们编写了csFile类
这里把文件名fileName和路径包含在构造函数里,也可以用重载的OpenForRead和OpenForWrite语句把它传递进来.
public class csFile{
private string fileName;
StreamReader ts;
StreamWriter ws;
private bool opened,writeOpened;
public csFile(){
init();
}
private void init(){
opened=false;
writeOpened=false;
}
public csFile(string file_name){
fileName=file_name;
init();
}
//打开用于读数据的文件
public bool OpenForRead(string file_name){
fileName=file_name;
try{
ts=new StreamReader(fileName);
opened=true;
}
catch(FileNotFoundException e){
return false;
}
return true;
}
//接下来,可以用readLine方法从文本文件读数据
public string readLine(){
return ts.ReadLine();
}
//打开一个用于写数据的文件,并向其中写入若干文本:
public void writeLine(string s){
ws.WriteLine(s);
}
public bool OpenForWrite(string file_name){
try{
ws=new StreamWriter(file_name);
fileName=file_name;
writeOpened=true;
return true;
}
catch(FileNotFoundException e){
return false;
}
}
}
在任何需要读写文件时,都会使用这个简化的文件方法包装类.