第8章 顺序文件的维护
对于专业人员,这是重要的一章。因为它介绍了顺序文件管理的平衡线算法。这是一个完全通用的过程,允许多笔交易对应于同一交易关键字,也可以对应于多个交易文件。这是银行晚间批处理要做的主文件更新工作的最重要部分,这个算法对于那些必须编制“顺序文件维护”程序的人是一定要弄懂的。
在本章的开始,我们首先介绍了COBOL本身对文件处理的的语法成分,描述和处理顺序文件的语法要点。接下来,我们给出了银行主文件修改的需求说明,并用伪代码表达了解决问题的平衡线算法。我们编制了两个版本的COBOL程序,第1个程序只完成主文件的修改;第2个程序则说明好的程序是容易维护的,我们在原来程序的基础上增加了一个交易日报表,并说明新增加的功能可以完全不用修改原来的主文件逻辑,只是在必要的地方加上交易日报表的处理而已。报表的处理中使用了二级中断(Break),一级在分行上,另外一级在账号上。
我们可以看到,如果原来的程序遵守正确的设计原则,就能够容易地实现对程序说明书较大的修改。因此,这一章是说明早期的结构理论怎样应用于COBOL环境的好例子。关于结构程序设计,我们将在第10章做专门的讨论。
这一章论述平横线算法,一种文件维护的通用方法。我们使用一个银行主文件更新的例子,说明平衡线算法的实际应用。一共给出了两个程序例子,来说明平衡线算法的使用及报表的处理方法。
8.1 COBOL的扩充
现在,我们要介绍在商业环境中处理顺序文件用到的COBOL结构。因此,我们要仔细地论及SELECT语句、I-O-CONTROL段。完整的FD项目及OPEN和CLOSE语句的扩充。对于另外的信息,读者请参阅相关的COBOL语言参考书。
8.1.1 SELECT语句
环境部的输入输出节(Input-Output Section)的具体内容跟文件所使用的组织形式和存取方法有关。它包含两个段。
● 文件控制(FILE-CONTROL)段。
● 输入输出控制(I-O-CONTROL)段。
文件控制段的格式为FILE-CONTROL段名(Paragraph-Name)。关键字FILE-CONTROL只能在文件控制段的开始位置出现一次,它必须从区域A(Area A)开始,并以句号(.)结束。文件控制段将程序中使用的逻辑名与外部的文件名关联起来。文件控制段必须包含从区域B(Area B)开始的SELECT语句,并以句号(.)结束。
COBOL标准认识3种类型的文件组织(顺序、相对和索引)形式,对应的有3种不同的SELECT语句。对程序中用到的每个文件,都要有一个对应的FD描述在数据部(Data Division)中。对顺序文件的完整的SELECT语句具有格式如下。
SELECT短语必须出现在最前面,其他的短语可以按任何顺序出现。OPTIONAL短语意味着,程序运行时文件不必存在。RESERVE短语通过提供可选的I/O区域(缓冲区)增加处理效率,除非有特别的命令,绝大多数编译保留至少一个可选的区域。ORGANIZATION和ACCESS MODE短语默认是SEQUENTIAL,因此,对于顺序文件,这些短语常常是省略的。但是,对于其他组织类型的文件,比如索引文件,它们是必需的。FILE STATUS短语允许人们监控每一个I/O请求的执行,在每次对文件操作后总检查它的状态是个好习惯。
文件控制(File-Control)段将程序中的每个逻辑文件与外部物理文件通过运行作业流联系起来。并不是所有的选项适合所有的运行平台。如果你想知道那些选项适合你的运行平台,就要检查随机的参考手册。
SELECT OPTIONAL只适应于打开(OPEN)方式为INPUT、I-O或EXTENDED模式的文件。对于那些程序运行时不一定存在的文件,必须指定SELECT OPTIONAL选项。
● file-name必须在数据部有一个FD项目与其对应。
● ASSIGN短语将程序中用到的逻辑文件名与实际的外部文件名对应起来。对于VSE系统,ASSIGN短语将逻辑文件名与SYSnnn(nnn是逻辑设备编号)对应起来;对于MVS或ZOS,ASSIGN短语将逻辑文件名与作业流的DD名对应起来。
● RESERVE短语指定运行时要分配给该文件的输入/输出缓冲区的大小。
● ORGANIZATION短语指定文件的逻辑结构而不管你对文件的处理方法如何。
● ACCESS MODE短语定义文件中记录的处理方法。对于顺序文件,唯一合法的处理方法是顺序(Sequential)。但是,对于索引和相对文件,处理方法则不同。
● ACCESS MODE IS SEQUENTIAL可以适用于所有3种文件。
✧ 顺序(Sequential)文件:文件中的记录只能按照它们建立时的顺序存取。
✧ 索引(Indexed)文件:文件中的记录是按照记录关键字(Key)值的升序或降序存取的。
✧ 相对(Relative)文件:文件中的记录是按照文件中记录的相对记录号的升序或降序存取的。
● ACCESS MODE IS RANDOM适用于索引和相对文件。
✧ 对于索引(Indexed)文件,记录关键字(Record Key)数据项的值指定要存取的记录。
✧ 定义相对(Relative)文件,相对记录键(Relative Key)中的值指定要存取的记录。
● ACCESS MODE IS DYNAMIC适用于索引和相对文件。
✧ 对于索引(Indexed)文件,文件中的记录既可以随机存取(指定记录键值),也可以顺序存取,最终采用哪种存取方法取决于所使用的输入输出语句的格式。
✧ 对于相对(Relative)文件,文件中的记录可以顺序存取,也可以随机存取(指定相对记录键的值),究竟使用何种存取方式取决于所使用的输入输出语句的格式。
● FILE STATUS短语用来指定文件输入输出语句操作的返回码,返回码的具体含义请查阅第9章中的描述。FILE STATUS的值应该在每次输入输出操作后检查。
下面是我们后面要用到的平衡线算法的交易文件的SELECT语句及FILE STATUS使用的例子。
000010 INPUT-OUTPUT SECTION. 000011 FILE-CONTROL. 000012 SELECT BKTRAN-FILE ASSIGN TO UT-S-BKTRAN 000013 FILE STATUS IS WS-BKTRAN-STATUS 000014 . … 000023 FD BKTRAN-FILE 000024 LABEL RECORDS ARE STANDARD 000025 RECORDING MODE IS F 000026 RECORD CONTAINS 80 CHARACTERS 000027 BLOCK CONTAINS 0 RECORDS 000028 DATA RECORD IS BKTRAN-RECORD. 000029 COPY BKTRAN REPLACING ==:BKTRAN:== BY ==BKTRAN==. … 000055 01 WS-FILE-STATUS. 000056 05 WS-BKTRAN-STATUS PIC X(02). … 000062 OPEN INPUT BKTRAN-FILE … 000065 IF WS-BKTRAN-STATUS NOT = '00' 000066 DISPLAY 'BKTRAN FILE ERROR:' WS-BKTRAN-STATUS 000067 END-IF
输入输出控制(I-O-CONTROL)段是可选的,其格式为I-O-CONTROL paragraph-name。它指定外部数据集与COBOL程序之间有效传递数据所需要的信息。I-O-CONTROL段制定重运行点,不同文件的公共内存区域及磁带上各个文件的地址。它的一般格式是:
RETURN短语建立一检测点,即运行期间给定程序状态的“快照”。故意用于运行时间长的程序,所以一旦程序不正常结束,它能从监测点而不是从开始点重启动。建立检测点通常是相对容易的,但常常难于从检测点启动作业。此外,问题可能在检测点之前已经出现,在这种情形下,重启动就是不值得的。方便是存在的,但使用时应当谨慎。
SAME AREA短语引起列举的文件对于它们的I/O应用程序共享相同的存储位置。而那种能力在10~15年前,当存储器价格甚贵时,也许是有用的,但今天必要性就小了。此外,它的使用也许在程序逻辑中引进不必要的复杂化,因为列举的文件不能同时打开。
当磁卷上有多个文件时,MULTIPLE FILE短语用来说明文件出问题的位置(同样的功能也能通过操作系统作业流的LABEL参数完成)。
8.1.2 在COBOL程序中使用ddname
在程序中使用作业流中的ddname的格式跟你使用的程序设计语言是紧密相关的,不同的程序设计语言,使用DDNAME的方法是不同的。在COBOL语言中,我们要将JCL中的ddname写在COBOL程序环境部的SELECT语句的assign短语中,它用来标识要使用在作业流的DD语句中的ddname。
比如,我们后面介绍的银行主文件更新程序中的SELECT语句如下。
SELECT FILEIN ASSIGN TO DD1... 000012 SELECT BKTRAN-FILE ASSIGN TO UT-S-BKTRAN 000013 FILE STATUS IS WS-BKTRAN-STATUS 000014 .
对应地,运行程序的作业流的ddname要与SELECT语句ASSIGN短语中的名字一致,即都为BKTRAN。
000023 //GOBKUPD EXEC PGM=BKUPD1 000029 //BKTRAN DD DSN=IBMUSER.TEST.BKTRAN.SORT,DISP=SHR
作业流中的ddname、COBOL程序的SELECT语句和物理文件之间的关系,可以用图8.1来表示。
图8.1 作业流中的ddname、COBOL程序的SELECT语句和物理文件之间的关系
8.1.3 FD项目
文件节(File Section)是数据部的一部分,它定义文件的记录格式,程序中用到的每个文件都要在FD中描述。每个文件名在程序中必须是唯一的,每个文件必须有一个或多个记录格式描述紧跟在FD项目后面。多个记录格式暗示它们是重定义的,FD项目中的各个短语的出现顺序没有限制。文件节定义数据文件的记录结构,文件节必须由FILE SECTION开头并以句号(.)结束。
下面显示的是顺序文件的FD项目具有的格式。
FD项目提供的信息包括:
● 文件描述项,提供文件名和文件的物理结构及相关的记录名。
● 记录描述项,由一组数据描述项描述特定文件的记录格式。一个文件可以指定多个记录格式,它们使用同一个记录存储区。在文件节(File Section)中描述的数据区域只有在相关的文件打开(Open)以后才能使用。
● file-name必须跟在FD的后面,并且要与相关的SELECT短语中的名字一致。file-name必须遵照COBOL标识符的命名规则,即至少有一个字符是字母,file-name在程序内必须是唯一的。跟在file-name后面的短语是可选的,它们可以按任意顺序出现。
BLOCK和RECORD CONTAINS短语提供4种不同格式的定长或变长度记录:固定长度记录,可以是分块的,也可以是不分块的;变长记录,可以是分块的,也可以是不分块的。允许很大的灵活性,人们必须仔细地考虑精确的目的。例如,注意下面的项目配对是不等价的。
BLOCK CONTAINS 5 RECORDS RECORD CONTAINS 42 TO 342 CHARACTERS
与
BLOCK CONTAINS 210 TO 1710 CHARACTERS RECORD CONTAINS 42 TO 342 CHARACTERS
第1对说明,每块正好包含5个记录。第2对说明每块包含210到1710个字符,因此如果记录长度较小的话,就能包含5个以上的记录。
BLOCK CONTAINS短语的省略引起编译假定,记录是不分块的,并等价于BLOCK CONTAINS 1 RECORD(项目BLOCK CONTAINS 0 RECORDS在IBM OS操作系统下是常见的,并意味着,分块大小将在JCL运行时输入)。
LABEL RECORDS短语是FD中唯一要求的项目。LABEL RECORDS ARE STANDARD暗示使用标准标号,并执行标号处理。LABEL RECORDS ARE OMITTED意味着,要么省略标号,要么它们不是标准的(亦即用户标号);在这两种情形下,不执行标号处理。
当使用标号时,系统必须有另外的信息使它执行标号处理。在IBM操作系统下,信息是在JCL中制定的。在其他的系统中,是在FD的VALUE短语中提供的。考虑项目:
FD OLD-MASTER-FILE …… LABEL RECORDS ARE STANDARD VALUE OF TAPE-LABEL IS 123456.
TAPE-LABEL是实现者的名字(即系统名字,在给定出版系统中指示可用的特性。环境部的SPECIAL-NAMES段将实现者的名字与程序员指定的记忆名联系起来)。这些项目的目的是保证COBOL文件OLD-MASTER-FILE,将从指定的带卷上读出。
8.1.4 OPEN和CLOSE语句
对顺序文件的一般格式如下所示。
OPEN和CLOSE动词的动作取决于文件FD中LABEL RECORDS短语的说明。如果使用标准标号,OPEN初始化标号处理。在输入文件中,检查头标号与JCL(IBM系统)中的信息或FD value短语(非IBM系统)中的信息。当打开输出文件时,就写进适当的头标号。CLOSE语句初始化尾标号是类似的过程,亦即它检查输入文件的尾标号,并对输出文件写一新的尾标号。如果FD包含LABEL RECORDS ARE OMITTED,就不执行标号处理。
输入文件的OPEN语句的最简单形式是OPEN INPUT file-name,它自动地引起重卷带。如果指定NO REWIND,就抑制重卷带(当许多文件包含在同一卷上时,就用后者)。REVERSED可选项引起文件相反的顺序处理,从最后一个记录开始(这并不是在所有带驱动器上都是可能的;再说,这一可选项只对单卷文件适用)。
OPEN语句的EXTEND选项作用是限制为单卷文件。如果指定这一模式,随后的写语句将在文件现存的最后记录后面增加记录,就像文件按OUTPUT方法打开一样(通过在OS JCL中编制DISP=MOD也达到同样的效果)。
OPEN语句初始化文件的处理,在OPEN语句中至少要指定INPUT、OUTPUT、I-O或EXTEND短语中的一个。INPUT、OUTPUT、I-O和EXTEND短语可以以任何顺序出现。
● INPUT,指定文件打开的模式,允许输入(INPUT)操作。
● OUTPUT,表明所打开的文件用于输出(OUTPUT)操作,它用于指定程序要建立的文件。注意,不用为下列文件指定OUTPUT选项。
✧ 文件有记录。文件中的记录会被新数据所替代。如果OUTPUT短语用于已经包含记录的文件,则数据集必须定义为可以重复使用的(Reusable)并且不能有次索引。文件中的记录会被新数据置换而且在SELECT语句中的ALTERNATE RECORD KEY短语会被忽略。
✧ 在OS/390操作系统下,文件在JCL中定义为DD dummy,这时可能出现不可预知的结果。
● I-O,允许文件既做输入,又做输出用。I-O短语只适用于直接存取设备(Direct Access Devices)上的文件。
● EXTEND,允许文件既作为输入又作为输出用。如果新数据按照升序写入,则EXTEND短语只适用于顺序存取文件。在OS/390操作系统下,如果QSAM文件使用多个文件卷,不要使用EXTEND短语。如果你想在文件的结尾增补记录,又不能肯定文件是否存在,就要在用ENTEND模式打开文件之前使用SELECT OPTIONAL短语。这时,文件就会被建立(如果文件不存在)或增补(如果文件已存在)。
CLOSE语句结束已打开文件的处理。对程序中已经打开的每个文件都应该使用CLOSE语句关闭它们。CLOSE语句只能适用于已经打开的文件,CLOSE语句成功执行后,与该文件相关的记录区域就不再可用。如果CLOSE不成功,则对应的记录数据仍然可用。
CLOSE WITH LOCK短语确保程序执行时不能重复打开文件。CLOSE WITH REWIND短语确保卷(磁带)保持在当前位置以避免本程序或其他程序重新打开正在使用的磁带文件。
CLOSE语句的最简单格式是CLOSE file-name,在处理结束时,它自动倒带。NO REWIND选项用于多文件卷,并防止与CLOSE相关的自动倒带。REEL和UNIT说明引起卷关闭而不是文件关闭,用于多卷文件。