所谓随机存取是相对于我们前面讨论的顺序存取而言的,顺序存取要求我们给出当前记录块号和当前记录号,而使用随机存取功能时DOS要求我们换一种眼光看待文件,即将一个文件从头至尾全部看作是由记录组成,而不需先将文件分成记录块后。再分记录。这样做的目的是使我们可以更方便地读写文件中任意位置的数据,无需做更多的附加工作。我们先来看看使用随机存取功能时要使用FCB中哪些数据。
回顾一下前面给出的FCB数据结构图,可以看到在FCB的末尾有一个双字的数据,这个数据称为"相对记录号"。在使用随机存取功能前我们必须设置这个值使得操作系统知道我们要读取哪个记录。
值得注意的是一般情况下这个双字的数据只有前3个字节有效,最后一个字节总为0。这是因为文件的长度是有限制的,DOS要求文件长度要小于1073741824个字节。这里所说的"一般情况",其实指得是当一个记录的长度大于64字节的时候。我们可以验算一下:如果一个文件长度恰好是最大长度,即1073741824字节,而一个记录长度是64字节,那么这个文件包含的记录数应是16777216个,这个数恰好是2的24次方。也就是说如果用二进制表示这个数,就需要24个Bit,正好是三个字节。
这里就有了这样一个问题,如果记录长度小于64字节,那么DOS是否会使用整个双字呢?很遗憾,没有哪一本教科书上谈到了这个问题,笔者也不想讨论它。实话说吧,我没有做过实验研究这个问题,因为我的硬盘根本装不下2的30次方个字节。
DOS究竟如何处理这个数据?其实很简单,DOS会根据这个数据自己算出当前记录块号和当前记录号然后使用顺序存取功能取得相应的数据。换句话说,DOS要求我们改变对文件的认识,而它自己并未改变。下面是DOS提供的随机存取功能的应用方法。
功能号: 21H
用 途: 随机读取文件中的某个记录
参 数: DS:DX--打开的FCB首地址
返 回: AL = 0--读取成功
AL = 1--已读到文件尾
AL = 2--记录溢出段
AL = 3--读出部分记录
功能号: 22H
用 途: 随机将某个记录写入文件
参 数: DS:DX--打开的FCB首地址
返 回: AL = 0--成功写入
AL = 1--磁盘满
AL = 2--记录溢出段
看来这两个功能的调用方法和我们前面讨论过的一些功能有相同之处,如果大家掌握了前面的顺序存取功能,那么随机存取功能应该不难学会,无非是变变观念而已。下面这个程序演示了这两个功能的应用,这个程序名叫MIRROR,它可以将源文件反过来写入目的文件中。
;数据段定义与程序FCB7.ASM相同,此处省略 CODE SEGMENT ASSUME CS:CODE MAIN PROC FAR MOV AX,DATA ;初始化DS寄存器 MOV DS,AX MOV AH,0FH ;打开"源文件" MOV DX,OFFSET FCB1 INT 21H OR AL,AL ;文件正确打开了吗? JNZ ERR_EXIT ;文件没有正确打开,转ERR_EXIT MOV BX,DX ;BX指向FCB1 MOV WORD PTR [BX+0EH],1 ;设置记录长度为1字节 MOV AX,WORD PTR [BX+10H] ;AX寄存器取得文件长度低字 DEC AX ;文件长度-1 MOV WORD PTR [BX+21H],AX ;将AX寄存器填入FCB1的"相对记录号"域 MOV AH,0FH ;打开"目标文件" MOV DX,OFFSET FCB2 INT 21H OR AL,AL ;打开操作正确吗? JZ SET_REC ;正确打开文件,转SET_REC MOV AH,16H ;建立"目标文件" MOV DX,OFFSET FCB2 INT 21H OR AL,AL ;文件正确建立了吗? JNZ ERR_EXIT ;文件没有正确建立,转ERR_EXIT SET_REC: MOV BX,DX ;BX指向FCB2 MOV WORD PTR [BX+0EH],1 ;设置FCB2的记录长度为1字节 READ_REC: MOV AH,21H ;从"源文件"中随机读一个记录 MOV DX,OFFSET FCB1 INT 21H OR AL,AL ;数据读取正确吗? JZ WRITE ;正确读入数据,转WRITE JMP ERR_EXIT ;没有正确读入数据,转ERR_EXIT WRITE: MOV BX,DX ;BX寄存器指向FCB1 DEC WORD PTR [BX+21H] ;FCB1的"随机记录号"减1 MOV AH,22H ;将读入的数据写入"目标文件" MOV DX,OFFSET FCB2 INT 21H OR AL,AL ;数据正确地写入"目标文件"了吗? JNZ ERR_EXIT ;数据未能正确写入,转ERR_EXIT MOV BX,DX ;BX寄存器指向FCB2 INC WORD PTR [BX+21H] ;FCB2的"随机记录号"减1 JMP READ_REC ;继续处理下一个记录 ERR_EXIT: MOV BX,OFFSET FCB1 ;BX寄存器指向FCB1 MOV AX,WORD PTR [BX+10H] ;取得"源文件"的长度 MOV DX,WORD PTR [BX+12H] MOV BX,OFFSET FCB2 ;BX寄存器指向FCB2 MOV WORD PTR [BX+10H],AX ;令"目标文件"长度等于"源文件"长度 MOV WORD PTR [BX+12H],DX MOV AH,10H ;关闭"目标文件" MOV DX,OFFSET FCB2 INT 21H MOV AH,4CH ;结束进程 INT 21H MAIN ENDP CODE ENDS END MAIN
大家可以跟踪执行这两个功能,看看它们对FCB中的数据有哪些影响。仔细分析这个程序,就会发现使用随机存取功能可以很容易的读写文件中任意处的数据,看来如果我们将文件的结构规定的越简单,那么处理文件就会相对容易一些。
通过上面这几个程序例,我们会发现DOS提供的"顺序"和"随机"这两种存取方式存在一个共同的缺点就是无法一次读入多个记录,这很不便于我们编制程序。当然我们前面曾经讨论过一种解决方法,就是将一个记录的长度设为整个文件的长度,这样可以一次取到整个文件的数据。这个方法虽可行但不够灵活,有时我们需不定量的读取一批数据且又要保持记录的长度不变,那么使用这个方法就有所不便。所以我下面就想介绍一下DOS提供的又一组随机存取功能--随机块存取。
所谓块存取的意思就是说使用这一组功能可以指定一次读写的记录个数,比如一次从文件中读出16个记录,至于其它方面和随机存取功能相同,唯一要注意是就是要开辟出足够的DTA空间容纳这些数据。