这里介绍一种我建议没必要再使用的值类型——文件类型。它的用途很容易猜到,文件嘛,自然是IO方面用的。基本类型有两个,一个是file,另一个是Text。像在控制台上(Console)输出的文字,以及由控制台输入的字符,都是基本IO——早期的计算机是没有显示器的,控制台也被视同一个文本文件进行统一处理。

前面讲过,TypeInfo只对少数的类型不起作用,其中包括文件类型及其派生类型。但是SizeOf是起作用的,可以看到它们的体积都不小,那么都包含哪些内容呢?它们的类型原型可以在System.pas中找到,file类型实际上是TFileRec,而Text类型其实是TTextRec。

Text类型还有一个别名类型:TextFile。而file类型本身是定义一个无类型文件,可以以它为基础,派生出其它类型的文件,语法为:

TFileOfTypeT = file of T;

关于这两种文件的用法,可以参考Delphi的帮助文件,使用起来也都不复杂,RTL也提供了一些相当方便的库函数。这里,我想讲一下为什么我不推荐再使用它们了。

仔细观察Assign/FileMode/Reset/CloseFile这个模型,不难发现,在使用Reset来打开一个文件时,打开的方式是由FileMode决定的。而FileMode是一个System单元中的全局变量,这也就意味着,当使用多线程打开不同文件时,FileMode本身存在着跨线程的危险——换言之,由最初Pascal提供的IO模型不是线程安全的。如果想使用它,那可以把从FileMode到Reset设置为一个临界区,但是,本身Windows提供的文件打开方式是不需要线程同步的——所以这个方法非常不经济。当然,40多年前的编译器编写者没考虑到今天计算机应用的变法,也是可以谅解的。

那么应该使用什么呢?在Classes单元中封装了TFileStream类型,我们可以使用流的封装打开文件,并且实践证明也是非常方便的。那文本文件怎么办?按行读取的话,可以用TStringList一次性读到内存中(一般文本文件都不会太上,上M已经很少见了,而几M对于2GB的进程空间实在是微不足道)。事实上,由于文件编码的不同,在处理本文文件时会有很大程度的混乱。例如,我们中文使用区,可能会有GBK、Big5、UTF-8、UTF-16 LE几种常见的编码,而如果下载一个国外的代码,如果用中文编码打开,可能会有一些乱码。所以,我个人并不太喜欢处理不知道会不会碰到特例的文本文件,而像XML这类文件,自然有其它的封装去做。

关于文件类型,就简单介绍到这里了。如果你以前常用它们的话,为了养成更好的编程习惯,我建议还是不要再用它了——哪怕是单线程的程序。