ITEEDU

8.1.1 文件的存取

一个文件正常打开(或建立)之后,我们下面对文件进行存取的时候就不再依靠那个ASCIIZ串了,而完全依靠DOS给我们的文件句柄。这可以从表--中所列的读写功能的用法中看出一些眉目来。下面我们就来看一看文件的读写功能是如何应用的。请看程序STARMAP.ASM:

STARMAP.ASM
      ESCkey  equ       1bh	;ESC键的ASCII码
        data  segment
              assume    ds:data
       fname  db        'hzk16',0	;ASCIIZ串
      buffer  db        32 dup(0)	;文件缓冲区
         msg  db        Press any key to display the next star-map.',0dh,0ah
              db        ESCape to exit',0dh,0ah,24h
        data  ends
			
        code  segment
              assume    cs:code
        main  proc      far
              mov       ax,data	;初始化DS寄存器
              mov       ds,ax
			
              mov       dx,offset fname	;DX指向文件名的ASCIIZ串
              mov       ax,3d00h	;按"读取"方式打开文件
              int       21h
              jc        exit	;若打开操作出错,转EXIT结束程序
			
              mov       bx,ax	;将文件句柄置入BX寄存器
      loop1:
              mov       ax,0003h	;重置显示模式3以清除屏幕
              int       10h
			
              mov       ah,09h	;显示字符串MSG
              mov       dx,offset msg
              int       21h
			
              mov       ah,3fh	;准备从文件中读取数据
              mov       dx,offset buffer	;DX指向文件缓冲区
              mov       cx,32	;读取32字节
              int       21h
              jc        exit	;若读取操作出错,转EXIT结束程序
              cmp       ax,cx	;确实读出了32个字节吗?
              jb        exit	;若只读出部分数据,转EXIT结束
			
              call      disp	;调用DISP子过程处理读入的数据
			
              mov       ah,0	;等待键盘输入字符
              int       16h
              cmp       al,ESCkey	;输入的是ESC键吗?
              jnz       loop1	;不是ESC键,转LOOP1继续
       exit:
              mov       ah,4ch	;结束进程
              int       21h
        main  endp
			
        disp  proc      near	;DISP子过程
              push      bx	;保存BX寄存器
              mov       cx,16	;处理读入的16个字(32字节)
              mov       si,offset buffer	;SI寄存器指向文件缓冲区
			
              mov       dh,3	;设置光标的初始行列位置
              mov       dl,32
      loop2:
              mov       ah,2	;设置光标位置
              mov       bh,0
              int       10h
			
              lodsw     ;从文件缓冲区取得一个字的数据
              xchg      ah,al	;调整高、低字节的位置
              push      cx	;暂存CX寄存器中的计数值
              mov       cx,16	;处理一个字的16个数位
      loop3:
              shl       ax,1	;将取得的数据从移1位
              push      ax	;保存移位后的结果,腾出AX寄存器
              mov       ah,0eh	;选择10H中断的0EH功能
              jnc       next_dot	;若移出AX的数位为0,转NEXT_DOT输出空格
              mov       al,2ah	;AL寄存器中送入"*"
              jmp       output	;转OUTPUT输出AL寄存器中的字符
   next_dot:
              mov       al,20h	;AL寄存器送入空格符
     output:
              int       10h	;输出AL中的字符
              pop       ax	;恢复移位后的结果
              loop      loop3	;返回LOOP3处理下一个数位
			
              inc       dh	;准备将光标移动至下一行
              pop       cx	;恢复CX寄存器
              loop      loop2	;返回LOOP2处理下一个字的数据
              pop       bx	;恢复BX寄存器
              ret       ;返回主过程
        disp  endp
        code  ends
              end       main

程序中出现了一个新指令--XCHG,这个指令用于将两个数据的位置进行交换:

助记符:XCHG(Exchange)
用 途:将两个寄存器或寄存器与存储器中的数据互换。
格 式:XCHG 寄存器,寄存器
XCHG 寄存器,存储单元
XCHG 存储单元,寄存器
执 行:寄存器之间或寄存器与存储单元中的数据交换了位置。

STARMAP是一个很有趣的程序,出现在屏幕上那些"星号"组成了一个个特殊的符号,大家可以试着使用UCDOS或其它汉字系统的"区位"输入法从区位码"0000"开始输入汉字,再和这个程序输出的"星图"作一对比,就会发现"有趣"之处。

这个程序所揭示的不仅仅是有关读取文件内容的方法,还有一个很重要的知识点。这个知识点将在下一章详细加以讨论。我们现在主要来讨论有关文件的内容。

3FH功能用于从文件中读取数据,使用这个功能时只需将被操作的文件所对应的句柄填入BX寄存器即可。同利用FCB的那些功能相比,它具有这样一些特点:

(1)DS:DX寄存器指向一个预先定义的缓冲区,读入的数据将保存在这个缓冲区中。句柄功能不采用定义DTA的方式保存数据,这给程序的设计带来了一些灵活性,比如我们可以将文件读到内存中任意一个位置,只需变变DS:DX的指向即可,无需反复调用系统功能定义DTA。当然采用这种方法也有不太尽人意的地方,它不象定义DTA那样可以一劳永逸,你必须时刻注意DS:DX的指向。
(2)每次读入的字节数可在CX寄存器中任意设定,最多64K字节。这一点告诉我们需要改变对文件的看法,不能再将文件看成是由记录块和记录所构成,而应将其看作是一串"字节流"。我们可以存取其中任意长度的数据串。
(3)每次执行读取功能后DOS会把实际读入的字节数通过AX寄存器返回,所以一旦AX寄存器返回的数据小于CX寄存器中设定的数值,则说明已读完了所有的数据。
(4)3FH功能能否使用还要取决于打开文件时所设定的存取方式。如果你设定AX寄存器为3D01H打开一个文件,那么对不起,你无法使用3FH功能从这个文件中读取任何内容,因为这个文件是为了写入而打开的。最后一点就是出错处理处理问题,一个样,利用JC/JNC指令依CF标志的状态进行转移即可。

这些特点说起来挺多,其实大家只需用DEBUG跟踪一下这个程序就全明白了。