标题：文件句柄
简介
在文件I/O中，要从一个文件读取数据，应用程序首先要调用操作系统函数并传送文件名，并选一个到该文件的路径来打开文件。该函数取回一个顺序号，即文件句柄（file handle），该文件句柄对于打开的文件是唯一的识别依据。要从文件中读取一块数据，应用程序需要调用函数ReadFile，并将文件句柄在内存中的地址和要拷贝的字节数传送给操作系统。当完成任务后，再通过调用系统函数来关闭该文件。
特点
一个句柄就是你给一个文件，设备，套接字(socket)或管道的一个名字, 以便帮助你记住你正处理的名字, 并隐藏某些缓存等的复杂性。(在内部, 句柄类似C++语言的流(streams), 或BASIC中的I/O通道)句柄使你从不同的地方输入和输出给不同的地方都较容易。使Perl成为好语言的一个原因是它能和多个文件通讯并一次处理他们。对外部对象有好的符号名字是一个好语言的一个组成部分[1]。 
[1]其他使Perl是一个好语言的是: 它是8位的, 是可嵌入的, 你能通过扩展模式在Perl中嵌入其他程序。它是简明的，网络上容易使用。环境上是清楚的，容易对话。你能以许多不同的方法引用它(就象前面看到的)。总之，语言本身不是如此严格的结构，以至于你不能使它超出你的问题。又回到TMTOWTDI。 
你创建一个句柄，并通过open函数把它和一个文件联结。open有两个参数: 句柄和你想与它联结的一个文件名。Perl也给出一些预定义(和预打开的)句柄。STDIN是你程序的正常输入通道, 而STDOUT是你程序的正常输出的通道。STDERR是一个附加的输出通道, 以便当把输入转为输出时, 程序能给出一些说明[2]。 
[2]一般地, 这些句柄和你的终端联结, 所以你能输入你的程序并能看到, 但他们也可以和文件联结。Perl能给你这些预定义句柄, 因为你的操作系统已提供这些。在UNIX下, 进程从他的父进程(一般是一个shell)继承标准输入, 输出和错误。一个shell的责任之一是建立这些I/O流, 以便子进程不必考虑这些)。
既然你能为各种目的(输入, 输出, 管道)使用open函数创建句柄, 你就必须能指明你要做什么。就象在UNIX命令行一样, 你给文件名简单地加些字符。
open(SESAME, "filename"); #从已存在的文件读 
open(SESAME, "<filename");#显式地，同上面
open(SESAME, ">filename"); #创建一个文件并对它写 
open(SESAME, ">>filename"); #对已有的文件接着写 
open(SESAME, "| output-pipe-command"); #建立一个输出过滤
open(SESAME, "input-pipe-command |"); #建立一个输入过滤
就象你看到的, 你可以任意选名字。一旦打开句柄SESAME, 它就能被用于存取文件或管道, 直到它被显式地关闭(用close(SESAME)), 或对同一句柄的一系列open把这个句柄和另一文件联结[3]。
[3]打开一个已打开的句柄是隐式地关闭第一个文件, 使它对文件句柄不可取，并打开一个不同的文件。你必须小心这是你真正想做的。有时，偶然碰巧，比如，当你open($handle,$file)时, $handle正好包含空串(null)。确认设置$handle为某个单一个量, 否则你将对空句柄打开一个新的文件。
一旦你已为输入打开一个句柄(或你使用STDIN), 你就能使用"行读操作"<>, 读一行。这个也以钻石操作闻名，因为它的形状。这个钻石操作包含你想读的句柄(<SESAME>)[4]。使用STDID句柄读用户提供的答案, 如下: 
[4]空钻石操作<>, 将从命令行指定的所有文件读, 如果没有指定, 从STDIN读。(这是许多UNIX"过滤"程序的标准行为) 
print STDOUT "Enter a number: "; #请求输入一个数 
$number = <STDIO>; #输入一个数
print STDOUT "The number is $number"; #输出这个数
你明白我们给你的例子吗?在print语句中STDOUT做什么?这就是你使用一个输出句柄的方法之一。一个句柄可以作为print语句的第一个参数, 如果存在, 告诉往哪儿输出。在例子中，句柄是冗余的，因为输出已经是STDOUT。对于输入的缺省是STDIN, 对于输出的缺省是STDOUT。(在班级例子的18行, 我们为避免使你混淆, 我们省略了。) 
我们也有一件事使你不明白。如果你试上面的例子，你可以注意到你得到一个特别的空行。因为读时没有自动地从你的输入行中删除换行符(newline)(例如, 你输入"9")。对于这些情况，当你想删除换行符时，Perl提供chop和chomp函数。chop将不加区别地删除(并返回)传给它的最后一个字符, 而chomp只删除记录标识的末尾(一般地是""), 并返回这样删除的字符数。你将经常看到这样输入一行：
chop($number = <STDIN>); #输入一个数并删除换行符
意思是: 
$number = <STDIN>; #输入一个数 
chop($number); #删除换行符
mov
int
mov
int
endp
ends
end ah,9
21h
ah,4ch
21h
main 
；结束进程
这是一个不寻常的程序，它的特殊性就在于使用了"不寻常"的文件句柄。还记得前面我们讨论文件句柄时所遗留的那个问题吗？我们打开的第一个文件的句柄号是05H而不是00H，之所以这样是因为句柄号00H－04H已经被占用了。而且更为特殊的是这五个句柄不是赋予五个文件的，而是赋予五种硬件设备。这听上去好象越来越乱了，文件和硬件设备竟然又出现了某种联系，实在让人搞不明白。我想若要弄清楚这个问题，我们还是要从一些实际现象出发。
学过C语言的朋友一定接触过fprintf函数的这样一个用法：
fprintf(STDERR,"DANGER!!!......Found a VIRUS......");
本来，在STDERR这个位置上应该是指向一个文件的指针，这个指针的含义和我们现在讨论的文件句柄是一样的（我想也许汇编语言中的文件句柄就是C语言中文件指针的实质，我没有查过权威资料，这个看法是否正确还有待检验）。现在我们在这个位置上放了一个称为STDERR的常量，而且我们知道这样使用fprintf函数可以使引号中的文字出现在显示器上而不是某个文件中，而显示器又恰好是一个硬件设备。由此看来，文件、文件指针和硬件设备确实应该存在某种联系，这种联系并非在汇编语言程序设计中有体现，在C这样的高级语言中已经体现出来了。那么STDERR又是个什么东西呢？
这个常量出现在名为STDIO.H的一个包含文件中，在C语言中它被称为"标准错误输出设备（STanDard ERRor output device）"C语言中还有两个比较常用的常量，分别为STDIN和STDOUT，即标准输入和标准输出设备。这三种设备通常都与键盘和显示器有关，使用fprintf函数从STDIN读入数据时相当于等待键盘输入；而向STDOUT或STDERR输出的内容都会出现在显示器上。这就有点儿象我们刚刚给出的那个汇编程序，在那个汇编程序中我们在输出数据时使用了一个特殊的句柄，结果导致了所有的文字都出现于显示器上而没有写入什么文件中。
看来我们所使用的那个特殊句柄代表的是显示器而不是文件。我们所使用的DOS系统还有这样一个有趣的功能不知道你是否使用过，那就是"重定向"功能。如果在DOS状态下按下面这样的格式使用DIR命令，大家就会发现文件和设备确实具有某种不寻常的联系：
C:\DOS\>DIR>FILE.LST
本来会出现在显示器上的一行行文件名都跑到一个名叫"FILE.LST"的文件中去了。由此我们设想这样一个结论：我们通过调用DOS的文件句柄功能不仅可以操作文件，同样可以操作一些硬件设备。
这个结论是完全正确的，事实上文件与设备本身就具有一些相似的特点：文件可以读入或写出，而硬件设备同样可以"读入"（键盘）和"写出"（显示器、打印机）。既然它们具有这样相同的特性，当然可以使用同样的形式来操作，这就是DOS为我们提供了那五个特殊句柄的原因。我们下面就来详细谈谈这五个特殊的句柄：
表8-3列出了这五个句柄所代表的硬件设备，其中0、1、2三个句柄是我们最常用的。这五种设备在DOS启动之后就已经"打开"，所以我们可以直接使用这五个句柄而不必再编制代码将其打开。有关这五个特殊句柄还有一些更值得深思的地方，比如，我们能否使用3FH功能从句柄2所表示的设备中读入信息？能否使用40H功能向句柄3所表示的设备输出信息？我们能否用3EH功能关闭某个句柄所表示的设备？分析这样的问题也非难事，编个程序试一试即可。 
句 柄 设备名称 逻辑设备名 缺省设备 
00 标准输入设备 CON 键盘 
01 标准输出设备 CON 显示器 
02 标准错误设备 CON 显示器 
03 标准辅助设备 AUX 串行口 
04 标准列表设备 PRN 打印机 
至此有关文件操作的几个重要功能--建立、打开、读写、关闭--我们都已经讨论，不是很全面，没有深入的地方还要大家自行研究。我们下面的任务就是来讨论文件句柄的一些辅助功能，以此做为这一内容的结束。 
句柄是操作系统在生成对象时分配给对象的唯一标识。 通过句柄可以获取操作系统提供的服务。句柄不同于指针,如果你得到一个对象的指针,那你就可以在此对象上为所欲为了!于是系统不给你指针,而给用户一个加了限制的,用于跟踪对象的指针的标识——句柄!系统使用句柄向外提供服务就相对安全了！ 操作系统是通过API提供服务的，对于用户来说，句柄等同于对象指针，但实际上句柄和指针根本不是一回事!