第七章
档案读写与管理
「凡走过必留下痕迹」,所以生命才会有真理,生命也才会不平凡。
程序也是一样,若我们写好的程序,其最终的结果若是不留下任何痕迹,那么它的利用性就不高,而要让程序留下痕迹,供后人利用,则非透过档案的读写不可。
C#
要读写档案,必须引用一个新的命名空间:「
System.IO
」,若要指定编码方式,则另需引用「
System.Text
」这个命名空间,其用法如下:
using System.IO;
using System.Text;
|
放的位置,可以是在「
using System;
」这个叙述之前或之后皆可,以后若有引用新的命名空间,其放置的位置也是这样的原则,或者说,命名空间的引用不在乎彼此顺序。
StreamWriter
档案写入法
档案的写入基本的三个步骤:开启、写入、关闭。
开启写入档案的语法
StreamWriter
档案对象
= new StreamWriter(
文件名称
,
是否接续
,
编码方式
);
|
其中「档案对象」是一个名称,其命名方式和变量名称规则一样,而文件名称可以是一个字符串或一个字符串变量,不管其内容为何,都必须可以让系统接受为文件名称,也可以包含资料夹名称,例如以下是一些合法的
"test"
"exam.txt"
"d://work//test.txt"
至于「是否接续」,是一个布尔值,可以是「
true
」或「
false
」,若是「
true
」,表示写入的资料会接续在被写入的档案原有的数据的后面,若为「
false
」,则被写入的档案原有的内容会被清空后,再写入数据,而编码方式如表
7-1
所示,若省略编码方式,则采用
系统现行的编码方式
。
而数据要写入档案则和
Console
模式差不多,方法如下
档案对象
.WriteLine(
输出字符串
);
|
或
档案对象
.Write(
输出字符串
);
|
最后,若档案已经不再有写入的动作时,则必须加以关闭,以后才能要读取或更改其属性,关闭的语法为:
档案对象
.Close( );
|
表
7-1 C#
档案编码方式
编码方式
|
说明
|
System.Text.Encoding.Default
|
以系统现行的编码方式
|
System.Text.Encoding.ASCII
|
以
ASCII Code
作为编码方式
|
System.Text.Encoding.BigEndianUnicode
|
以
Big-Endian Unicode
作为编码方式
|
System.Text.Encoding.Unicode
|
以
Unicode
作为编码方式
|
System.Text.Encoding.UTF7
|
以
UTF7
作为编码方式
|
System.Text.Encoding.UTF8
|
以
UTF8
作为编码方式
|
注:实用上若已
using System.Text
,则可以省略表中的
System.Text
EX 7-1
请在一个新的
c:/test.txt
写入
「
123 456 789
」、「
987 654 321
」于两行,采用
unicode
编码。
【解】
StreamWriter MYFILE = new StreamWriter(
"c://test.txt"
, false,
Encoding.Unicode
);
MYFILE.WriteLine(
"
123 456 789
"
);
MYFILE.WriteLine(
"987
654 321
"
);
MYFILE.Close( );
|
执行后以记事本打开
c:/test.txt
,内容如下,刚刚的数据都写入了。

EX 7-2
请在
EX7-2
的
c:/test.txt
的后面写入
「结束了」。
【解】
StreamWriter MYFILE = new StreamWriter("c://test.txt",true, Encoding.Unicode);
MYFILE.WriteLine("
结束了
");
MYFILE.Close( );
|
执行上面的程序后以记事本打开
c:/test.txt
,内容如下,可以发现除了原先的数据,另外多了一行「
结束了
」。

EX7-3
产生
100
个介于
1
到
10000
的随机数,每
10
个一列,整齐地写入
c:/number.txt
【解】
StreamWriter NUMFILE = new StreamWriter("c://number.txt",false) ;
Random NUM = new Random();
int
i;
for
(i=1; i<101;i++)
{
NUMFILE.Write("{0,7:D}",NUM.Next(1,10001));
if
(i%10==0)NUMFILE.WriteLine();
}
NUMFILE.Close( );
|

StreamReader
档案读取法
档案的读取和写入的方式有一点像,
基本的三个步骤:开启、读取、关闭。
开启读取档案的语法:
StreamReader
档案对象
= new StreamReader(
文件名称
,
编码方式
);
|
档案对象
.ReadLine();
|
或
档案对象
.
.ReadToEnd
();
|
最后,若档案已经不再有读取的动作时,则必须加以关闭,以后才能被读取、写入或更改其属性,关闭的语法为:
档案对象
.Close( );
|
EX7-4
将
EX7-3
产生的
c:/number.txt
的内容读取,并显示于屏幕。
【解法一】逐行读取,前面我们已经知道写入
10
行,因此可以用循环逐行读入。
StreamReader MYREADER = new StreamReader("c://number.txt");
int
i;
string
s;
for (i=1;i<11;i++)
{
s=MYREADER.ReadLine();
Console.WriteLine(s);
}
|
【解法二】一次全部读取
StreamReader MYREADER = new StreamReader("c://number.txt");
int
i;
string
s;
s=MYREADER.ReadToEnd();
Console.WriteLine(s);
MYREADER.Close();
|
以上两个解法输出结果都相同,如图
7-4
所示。
逐行读取和一次读取各有应用上的需求,当字符串需要逐行处理时,还是以逐行读入较妥适,例如以下的例子:
EX7-5
请分不同行输出
EX7-3
的
c:/number.txt
里的第一列数字
【解】读入一行后以字符串处理的技术将个别数字字符串输出
StreamReader MYREADER = new StreamReader("c://number.txt");
int
i;
string
s;
string
[] num;
s=MYREADER.ReadLine();
num=s.Split(' ');//
以空白分割子字符串
for
(i=0;i<num.Length;i++)
if
(num[i].Trim()!="")Console.WriteLine(num[i]);//
不打印空白字符串
MYREADER.Close();
|

图
7-4

图
7-5
分不同行打印
mumber.txt
里的第一列的数字
二进制
档案写入法
数据串流(
data stream
)是把数据看成「字节」的连接,比较严谨的数据库,都应该是以串流的型态来读写。在
EX7-5
里,因为是以一行的方式读入字符串,再以字符串处理的方式解析内容,因此会花需多时间在字符串的解析,所以档案读取效率不佳。例如一个
32
位的整数写入档案,应该占档案
32
位,当我们读取数据时,若确认下一比的数据是
32
位的整数,那么只需精准地读入
32
位,再转换为整数即可,所以效率高。
C#
提供了宣告串流档案的方法,
串流档案的以
FileStream
宣告,
FileStream
串流档案对象
=new FileStream(
文件名称
,
档案建立模式
);
|
其中「串流档案对象」表示该档案将要以字节型态写入,而「文件名称」和前面所叙一样,至于「档案建立模式」的功用如表
7-1
所示。
档案建立模式
|
说明
|
FileMode.Append
|
在档案存在时开启它并搜寻至档案末端,或建立新档案。
|
FileMode.Create
|
指示操作系统应该建立新档案。如果档案已经存在,将覆写此档案。
|
FileMode.CreateNew
|
指示操作系统应该建立新档案。
|
FileMode.Open
|
指示操作系统应该开启存在的档案。
|
FileMode.OpemOrCreat
|
指示操作系统,如果档案存在应该开启档案;否则,应该建立新的档案。
|
虽然串流档案对象也有自己的读写档案的方式,但是太过于麻烦,因此有兴趣的读者请自行研究,作者不另再说明。
当串流档案宣告完成后,可以利用
BinaryWriter
和
BinaryReader
来读写,其中宣告二进制写入的方式是
BinaryWriter
二进制档案对象
= new BinaryWriter (
串流档案对象
);
|
而宣告二进制读取的方式是
BinaryReaderr
二进制档案对象
= new BinaryReader (
串流档案对象
);
|
写入数据的方法
二进制档案对象
.Write (
数值或字符串数据
);
|
读取数据的方法依照被写入数据的数据型态而定
二进制档案对象
.
读入数据类型
();
|
其中读入数据类型
ReadInt32
、
ReadDouble
、
ReadString
,如表
7-2
所示,仅列举部分读者可依数据型态种类自行尝试其它方法。
EX7-6
将
int a=1, double b=2.5, string s=”abcde”
以二进制方式写入和读取。
int
a=1;
double
b=2.5;
string
c="abcde";
FileStream WRITEFILE = new FileStream("c://binary.txt",FileMode.Create); //
注意
FileMode
BinaryWriter BINWRITE = new BinaryWriter(WRITEFILE );
BINWRITE.Write(a);
BINWRITE.Write(b);
BINWRITE.Write(c);
BINWRITE.Close();
WRITEFILE.Close();
FileStream READFILE = new FileStream("c://binary.txt",FileMode.Open);//
注意
FileMode
BinaryReader BINREAD = new BinaryReader(READFILE);
int
d=BINREAD.ReadInt32();
double
e=BINREAD.ReadDouble();
string
f=BINREAD.ReadString();
Console.WriteLine("{0} {1} {2}",d,e,f);
READFILE.Close();
BINREAD.Close();
|
表
7-2
二进制档案读入数据类型的方法和其它方法(部分列举)
方法
|
说明
|
用法(假设二进制档案对象是
a
)
|
Close
|
关闭目前的读取器
(Reader)
和基础数据流。
|
a.Close();
|
PeekChar
|
传回下一个可用字符,而不前移字节或字符的位置。
|
char b=(char) a.PeakChar();
|
ReadBoolean
|
自目前数据流读取
Boolean
值,并将数据流中目前的位置往前移一个字节。
|
bool c=a. ReadBoolean();
|
ReadByte
|
自目前数据流读取下一个字节,并将数据流中目前的位置往前移一个字节。
|
byte c=a.ReadByte();
|
ReadBytes
|
自目前数据流读取指定数目字节到字节数组,并将目前位置往前移指定数目字节。
|
byte [] d= a.ReadBytes(5);//
读入
5
个字节
|
ReadChar
|
按照所使用的
Encoding
和从数据流读取的特定字符,自目前数据流读取下一个字符,并将数据流中目前的位置往前移。
|
char b=(char) a. ReadChar ();
|
ReadChars
|
按照所使用的
Encoding
和从数据流读取的特定字符,自目前数据流读取指定数目字符,再将数据传回字符数组,并将目前位置往前移。
|
char [] e =a. ReadChars(2);//
读入
2
个字节
|
ReadDouble
|
自目前数据流读取八字节浮点数值,并将数据流目前位置前移八个字节。
|
double f =a. ReadDouble();
|
ReadInt32
|
自目前数据流读取四字节带正负号的整数,并将数据流目前位置前移四个字节。
|
int g= a.ReadInt32();
|
ReadSingle
|
自目前数据流读取四字节浮点数值,并将数据流目前位置前移四个字节。
|
float h =a. ReadSingle();
|
ReadString
|
自目前数据流读取字符串。字符串会以长度为前导符,每次以
7
位编码为一整数。
|
string s =ReadString();
|
档案的管理
在程序的进行中,有时候必需对某一些档案做处理,例如侦测档案的状态、改变档案的属性、删除档案、复制档案等,在进行这些操作时,同样必需用到「
System.IO
」这个命名空间,并且引用它的「
File
」,「
File
」之下有好几个方法,如表
7-3
所示,可以协助程序设计师开发档案管理的程序。
表
7-3
档案管理的重要方法(部分列举)
方法
|
说明
|
用法
(
假设
EX7-6
完成,
string a=
“c://binary.txt”
string b=“c://backup.txt”
)
|
Copy
|
复制现有的档案到新的档案。
|
File.Copy(a,b,true);//
允许覆写
File.Copy(a,b,false);//
不允许覆写
|
Delete
|
删除指定的档案。例外状况不会被掷回,如果指定档案不存在的话。
|
File.Delete(b);
|
Exists
|
判断指定的档案是否存在。档案存在会传回
true
,否则传回
false
。
|
Console.WriteLine(File.Exists(a));//
传回
true
|
GetAttributes
|
取得路径上档案的
FileAttributes
,如表
7-4
所示。
|
Console.WriteLine(File.GetAttributes(a));
|
GetCreationTime
|
传回指定档案或目录的建立日期和时间。
|
DateTime t= File.GetCreationTime(a);
|
GetLastAccessTime
|
传回指定档案或目录上次被存取的日期和时间。
|
DateTime t= File.GetLastAccessTime(b);
|
GetLastWriteTime
|
传回指定档案或目录上次被写入的日期和时间。
|
DateTime t= File.GetLastWriteTime(b);
|
Move
|
移动指定的档案至新的位置,提供指定新文件名的选项,但是新文件名若已存在会有错误讯息。
|
File.Delete(b);//
新档名已存在,先删除
File.Move(a,b);//
将
a
更名为
b
Console.WriteLine(File.Exists(aa));//
传回
false
,因为
a
已被重新命名为
b
|
SetAttributes
|
在指定路径上设定档案的指定
FileAttributes
。
|
File.SetAttributes(a,FileAttributes.ReadOnly);
|
SetCreationTime
|
设定档案建立的日期和时间。
|
DateTime newtime = new DateTime(2005,10,25);
File.SetCreationTime(a,newtime);
|
SetLastAccessTime
|
设定指定档案上一次被存取的日期和时间。
|
DateTime newtime = new DateTime(2005,10,25);
File.SetLastAccessTime(a,newtime);
|
SetLastWriteTime
|
设定指定档案上次被写入的日期和时间。
|
DateTime newtime = new DateTime(2005,10,25); File.SetLastWriteTime(a,newtime);
|
EX7-06
检查
EX7-05
产生的
c:/binary.txt
是否存在,若存在,为它制作一个备份名叫
c:/backup.txt
,并确实检查
c:/backup.txt
是否存在,再将
c:/binary.txt
删除,然后将
c:/backup.txt
重新命名为
c:/binary.txt
,并检查
c:/binary.txt
和
c:/backup.txt
存在的状况。
【解】
if(File.Exists(a))
{
Console.WriteLine("
档案
{0}
存在的状况为
{1}", a,File.Exists(a));
Console.WriteLine("
以下为档案
{0}
制作一个备份文件
{1}", a,b);
File.Copy(a,b,true);
Console.WriteLine("
接下来删除档案
"+a);
File.Delete(a);
Console.WriteLine("
接下来将档案
{0}
更名为
{1}", b,a);
File.Move(b,a);
Console.WriteLine("
档案
{0}
存在的状况为
{1}", a,File.Exists(a));
Console.WriteLine("
档案
{0}
存在的状况为
{1}", b,File.Exists(b));
}
|
执行画面如下:

表
7-4
档案属性
(FileAttributes)
列举
成员名称
|
说明
|
值
|
FileAttributes.
Archive
|
档案的保存
(Archive)
状态。应用程序使用这个属性标记档案来进行备份或移除。
|
32
|
FileAttributes.
Compressed
|
档案是压缩过的。
|
2048
|
FileAttributes.
Device
|
保留供将来使用。
|
64
|
FileAttributes.
Directory
|
档案是一个目录。
|
16
|
FileAttributes.
Encrypted
|
档案或目录是加密过的。对档案而言,这表示档案中的所有数据都被加密。对于目录而言,这表示加密
(Encryption)
是新建档案和目录的默认值。
|
16384
|
FileAttributes.
Hidden
|
档案是隐藏的,因此不会包括在一般目录的清单内。
|
2
|
FileAttributes.
Normal
|
档案是正常的,并且没有其它属性设定。这个属性只有在单独使用时是有效的。
|
128
|
FileAttributes.
NotContentIndexed
|
档案不会由操作系统的内容索引服务进行索引。
|
8192
|
FileAttributes.
Offline
|
离线档案。档案数据不是直接可供使用的。
|
4096
|
FileAttributes.
ReadOnly
|
档案是只读的。
|
1
|
FileAttributes.
ReparsePoint
|
档案包含重新剖析的位置,它是与档案或目录有关联的使用者定义的区块。
|
1024
|
FileAttributes.
SparseFile
|
档案是疏松档案。疏松档案基本上为其数据几乎为零值的大型档案。
|
512
|
FileAttributes.
System
|
档案是系统档案。档案为操作系统的一部份,或为操作系统所专用。
|
4
|
FileAttributes.
Temporary
|
临时文件。当不再需要时,暂存盘应该由应用程序删除。
|
256
|