COBOL (Common Business-Oriented Language)
COBOL (Common Business-Oriented Language) 是一种面向事务处理的、高级的、面向过程的编程语言,专为商业、金融和行政管理领域的数据处理而设计。自其诞生以来,COBOL 在企业级应用中占据了核心地位,尤其是在大型机系统上,承载着全球大部分的银行交易、保险理赔、政府记录和供应链管理。其设计哲学强调可读性、自文档化和对文件处理的强大支持,使其成为处理海量结构化数据的理想选择。
至于“CobolScript”,这通常不是指一种独立的、广泛认可的编程语言,而更多地可能涉及到:
-
COBOL 程序的脚本化运行环境:例如在大型机上通过 JCL (Job Control Language) 脚本来执行和管理 COBOL 程序。
-
现代编程语言与 COBOL 的集成:例如使用 Python 或 Java 编写脚本来调用或与 COBOL 程序进行交互。
-
一种概念性的尝试或小众项目:探索将 COBOL 的特性与脚本语言的灵活性结合,但目前没有形成主流。
本篇文档将深入探讨 COBOL 语言的方方面面,包括其历史、结构、语法、特性、应用、优缺点,以及它在现代计算环境中的演变和地位。最后,我们将讨论与“CobolScript”相关的概念,探讨 COBOL 与脚本化环境的结合方式。
第一章:COBOL的起源与发展历史
COBOL 的历史是计算机科学史上一个引人入胜的篇章,它见证了计算机从科学计算的利器转向商业数据处理核心工具的转变。
1.1 诞生前的背景
在1950年代,计算机主要用于科学和军事计算。编程语言如 Fortran 专注于数值运算和科学公式。然而,随着商业组织开始认识到计算机在处理大量交易数据方面的潜力,对一种更适合商业数据处理的语言的需求日益增长。当时的商业应用通常用汇编语言编写,这不仅耗时耗力,而且代码难以阅读、维护和移植。不同的硬件平台有不同的汇编语言,导致程序无法跨机器运行。
1.2 CODASYL与Grace Hopper的角色
1959年,美国国防部召集了一次会议,旨在讨论创建一种通用的商业语言。此次会议促成了数据系统语言会议 (Conference on Data Systems Languages, CODASYL) 的成立。CODASYL 是一个由政府、工业和学术界的代表组成的委员会,其主要任务是开发和标准化这种新的商业语言。
在 COBOL 的诞生过程中,海军少将 Grace Murray Hopper 扮演了关键角色。她是一位计算机科学的先驱,被誉为“计算机软件工程之母”。Hopper 及其团队开发的 FLOW-MATIC (后来成为 AIMACO) 语言对 COBOL 的设计产生了深远影响,尤其是在数据声明和面向英语的语法方面。她坚持认为编程语言应该使用接近人类自然语言的表达方式,以便非专业的业务人员也能理解代码。
CODASYL 委员会在极短的时间内工作,于1959年公布了 COBOL 的第一个规范,并在1960年推出了第一个版本 COBOL-60。这个速度在当时是前所未有的,凸显了对标准化商业语言的迫切需求。
1.3 标准化与演进
COBOL 的早期版本存在一些不兼容性问题,因为不同的编译器厂商对规范有不同的解释。为了解决这个问题,美国国家标准协会 (American National Standards Institute, ANSI) 于1968年发布了 ANSI COBOL 68,这是第一个官方的 COBOL 标准。
此后,COBOL 经历了多次重要的修订和标准化:
-
ANSI COBOL 74:引入了文件操作的改进,如索引文件和相对文件支持。
-
ANSI COBOL 85:这是 COBOL 历史上最重要的版本之一,引入了结构化编程的特性,如
EVALUATE语句、内联PERFORM、END-IF、END-PERFORM等,极大地提升了代码的清晰度和可维护性。许多至今仍在运行的 COBOL 程序都是基于 COBOL 85 标准或其后续的小幅修订。 -
COBOL 2002:这是一个里程碑式的版本,首次引入了面向对象 (Object-Oriented, OO) 特性,包括类、对象、继承、多态等,试图将 COBOL 现代化并与 Java、C++ 等语言对齐。此外,还增加了对 XML 和更多的网络通信支持。
-
COBOL 2014:在 COBOL 2002 的基础上进行了小幅改进和澄清。
1.4 大型机时代的辉煌
在整个20世纪下半叶,COBOL 是大型机上的主要编程语言。全球绝大多数的银行、保险公司、航空公司、政府机构和大型企业的核心业务系统都是用 COBOL 编写的。其在数据处理、事务管理和报表生成方面的强大能力,使其成为这些关键业务流程的基石。COBOL 程序的稳定性、可靠性和处理海量数据的能力,使其在大型机环境中无可替代。
1.5 Y2K危机与COBOL的再发现
进入20世纪末,千年虫问题 (Y2K bug) 的出现使得 COBOL 再次成为全球关注的焦点。许多早期的 COBOL 程序为了节省存储空间,使用两位数字来表示年份 (例如,98 代表 1998 年)。当年份进入 2000 年时,这些程序可能会将 00 解释为 1900 年而不是 2000 年,从而导致系统崩溃或数据错误。
为了解决 Y2K 问题,全球范围内投入了数千亿美元用于审查、修改和测试数以亿计的 COBOL 代码行。这场危机凸显了 COBOL 代码在关键业务系统中的巨大存量和不可替代性,同时也促使许多人重新审视这门“老旧”的语言。Y2K 项目不仅挽救了许多系统,也培养了一大批新的 COBOL 程序员和维护人员。
1.6 现代COBOL与未来展望
尽管面临新语言的竞争和技术趋势的演变,COBOL 至今仍然活跃。全球每年有数万亿的交易通过 COBOL 系统处理。许多大型企业仍在维护和扩展其 COBOL 应用程序。
现代 COBOL 的发展方向包括:
-
面向对象 COBOL:如 COBOL 2002 标准所定义的,允许 COBOL 程序采用现代面向对象的设计范式。
-
跨平台支持:除了大型机,COBOL 编译器和运行时环境也支持 Unix、Linux、Windows 等开放系统。GnuCOBOL (原名 OpenCOBOL) 是一个流行的开源 COBOL 编译器,它使得 COBOL 能够在 GNU/Linux 和其他类 Unix 系统上运行,并可以与 C 语言进行互操作。
-
与现代技术的集成:通过中间件、Web 服务和 RESTful API,COBOL 应用程序可以与 Java、.NET、Python 等现代技术栈进行集成,实现数据交换和功能调用。
-
云部署:将 COBOL 应用程序迁移到云计算平台,如 IBM Z and Cloud Modernization Stack、Micro Focus Enterprise Suite 等,使其能够利用云的弹性、可伸缩性和成本效益。
尽管 COBOL 社区面临人才老化和新一代开发者兴趣不足的挑战,但其在关键业务领域的深厚根基和极高的可靠性确保了它在可预见的未来仍将扮演重要角色。
第二章:COBOL程序的基本结构
COBOL 程序的结构非常严谨和模块化,它被划分为四个主要的**“部 (DIVISION)”**。每个部都有特定的功能和用途,并且必须以特定的顺序出现。这种结构强制了代码的组织性,提高了可读性和可维护性。
一个基本的 COBOL 程序结构如下:
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO-WORLD.
AUTHOR. YOUR-NAME.
DATE-WRITTEN. 2023-01-01.
DATE-COMPILED.
SECURITY. NONE.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-MAINFRAME.
OBJECT-COMPUTER. IBM-MAINFRAME.
SPECIAL-NAMES.
C01 IS NEW-PAGE
STANDARD-1 IS MY-ASCII-CODE.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INPUT-FILE ASSIGN TO 'SYSIN'
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL.
SELECT OUTPUT-FILE ASSIGN TO 'SYSOUT'
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL.
I-O-CONTROL.
APPLY WRITE-VERIFY ON OUTPUT-FILE.
DATA DIVISION.
FILE SECTION.
FD INPUT-FILE
RECORD CONTAINS 80 CHARACTERS
LABEL RECORDS ARE STANDARD.
01 INPUT-RECORD PIC X(80).
FD OUTPUT-FILE
RECORD CONTAINS 80 CHARACTERS
LABEL RECORDS ARE OMITTED.
01 OUTPUT-RECORD PIC X(80).
WORKING-STORAGE SECTION.
01 WS-HEADING.
05 FILLER PIC X(7) VALUE "HELLO, ".
05 WS-NAME PIC X(10) VALUE "WORLD".
05 FILLER PIC X(1) VALUE "!".
01 WS-COUNTERS.
05 WS-RECORD-COUNT PIC 9(5) VALUE ZEROS.
05 WS-ERROR-FLAG PIC X(1) VALUE 'N'.
88 WS-ERROR-OCCURRED VALUE 'Y'.
LINKAGE SECTION.
01 LS-INPUT-PARAM PIC X(20).
01 LS-OUTPUT-RESULT PIC 9(5).
PROCEDURE DIVISION.
MAIN-LOGIC SECTION.
BEGIN-PROGRAM.
DISPLAY WS-HEADING.
PERFORM VARYING WS-RECORD-COUNT FROM 1 BY 1 UNTIL WS-RECORD-COUNT > 5
DISPLAY "Processing record " WS-RECORD-COUNT
END-PERFORM.
OPEN INPUT INPUT-FILE.
IF FILE-STATUS OF INPUT-FILE = '00'
READ INPUT-FILE AT END
DISPLAY "End of input file."
GO TO END-PROGRAM
NOT AT END
DISPLAY "First record: " INPUT-RECORD
END-READ
ELSE
DISPLAY "Error opening input file. Status: " FILE-STATUS OF INPUT-FILE
END-IF.
CLOSE INPUT-FILE.
ACCEPT WS-NAME.
DISPLAY "You entered: " WS-NAME.
COMPUTE WS-RECORD-COUNT = (5 + 3) * 2 / 4.
DISPLAY "Calculated value: " WS-RECORD-COUNT.
END-PROGRAM.
GOBACK.
2.1 标识部 (IDENTIFICATION DIVISION)
这是 COBOL 程序的第一个部,也是唯一一个不需要句号(.)的部。它提供了关于程序的基本信息。
-
PROGRAM-ID.:这是唯一必需的段落。它指定了程序的名称,最多可包含 30 个字符(字母、数字、连字符)。这个名称在程序编译时会被用作可执行文件的名称。 -
AUTHOR.:程序的作者姓名。 -
INSTALLATION.:程序运行的机构名称。 -
DATE-WRITTEN.:程序的编写日期。 -
DATE-COMPILED.:程序编译的日期。这个通常由编译器在编译时自动填充。 -
SECURITY.:程序的保密级别或其他安全信息。 -
REMARKS.:程序的总体描述或目的。
这些信息对于文档化和团队协作非常重要。
2.2 环境部 (ENVIRONMENT DIVISION)
这个部描述了程序运行的环境,包括硬件、输入/输出设备以及文件组织方式。它是唯一一个与特定机器环境相关的部,因此当程序从一个机器移植到另一个机器时,通常需要修改这个部。
2.2.1 配置节 (CONFIGURATION SECTION)
-
SOURCE-COMPUTER.:程序编译时所用的计算机。 -
OBJECT-COMPUTER.:程序运行时所用的计算机。 -
SPECIAL-NAMES.:-
currency sign is character:定义程序中使用的货币符号。 -
ALPHABET IS alphabet-name FOR ...:定义字符集的名称及其映射。 -
CLASS class-name IS ...:定义一个字符类,例如CLASS DIGIT-CHARS IS '0' THRU '9'. -
[function-name IS mnemonic-name]:将特定功能(如换页)与一个助记符关联起来,用于WRITE ... AFTER ADVANCING语句。例如,C01 IS NEW-PAGE允许WRITE PRINT-LINE AFTER ADVANCING NEW-PAGE。
-
2.2.2 输入输出节 (INPUT-OUTPUT SECTION)
-
FILE-CONTROL.:这是最重要的段落,它将程序内部的文件名与外部系统文件或设备名称关联起来,并定义文件的组织方式和访问模式。-
SELECT file-name ASSIGN TO external-name:声明一个文件,并将其与外部系统名(例如大型机上的 DDNAME 或操作系统上的文件名)关联。 -
ORGANIZATION IS SEQUENTIAL / INDEXED / RELATIVE:指定文件的组织方式。-
SEQUENTIAL:文件记录按物理顺序存储和访问。 -
INDEXED:文件通过一个或多个键进行组织和访问(类似于数据库表)。 -
RELATIVE:文件记录通过其相对记录号进行组织和访问。
-
-
ACCESS MODE IS SEQUENTIAL / RANDOM / DYNAMIC:指定文件访问模式。-
SEQUENTIAL:只能顺序访问记录。 -
RANDOM:可以随机访问记录(通常用于索引文件)。 -
DYNAMIC:可以混合顺序和随机访问(通常用于索引文件)。
-
-
RECORD KEY IS data-name:对于索引文件,指定主键。 -
ALTERNATE RECORD KEY IS data-name [WITH DUPLICATES]:对于索引文件,指定备用键。 -
FILE STATUS IS data-name:指定一个数据项来接收文件操作的状态码。这是 COBOL 中进行文件错误处理的关键机制。常见的状态码包括 '00' (成功), '10' (文件尾), '23' (文件未找到), '90' (其他错误) 等。
-
-
I-O-CONTROL.:提供了对输入/输出操作的额外控制,如检查点、区域分配等。-
APPLY WRITE-VERIFY ON file-name:在写入操作后进行校验。 -
MULTIPLE FILE TAPE CONTAINS file-name:用于处理多文件磁带。
-
2.3 数据部 (DATA DIVISION)
这是 COBOL 程序的另一个核心部分,它定义了程序中所有使用的数据,包括文件记录、变量、常量等。COBOL 的数据部以其详细的数据描述能力而闻名,几乎所有的内存分配和数据布局都由程序员通过 PICTURE 子句和 USAGE 子句来显式控制。
2.3.1 文件节 (FILE SECTION)
文件节定义了程序将要处理的所有文件及其记录结构。
-
FD (File Description):用于描述文件的属性。-
file-name:在SELECT语句中定义的文件名。 -
RECORD CONTAINS integer CHARACTERS:指定文件记录的固定长度。也可以是RECORD CONTAINS integer-1 TO integer-2 CHARACTERS用于变长记录。 -
LABEL RECORDS ARE STANDARD / OMITTED:-
STANDARD:表示文件有标准的卷标和文件标,通常用于磁带文件。 -
OMITTED:表示文件没有标准的卷标和文件标,通常用于打印文件或临时文件。
-
-
BLOCK CONTAINS integer RECORDS / CHARACTERS:定义文件的块大小,用于优化物理I/O。 -
DATA RECORDS ARE data-name-1 [data-name-2 ...]:指定文件中包含的记录名。
-
-
在
FD之后,紧跟着定义文件的记录结构,使用 01 级数据项。
FD CUSTOMER-FILE
RECORD CONTAINS 100 CHARACTERS
LABEL RECORDS ARE STANDARD.
01 CUSTOMER-RECORD.
05 CUSTOMER-ID PIC X(10).
05 CUSTOMER-NAME PIC X(30).
05 CUSTOMER-BALANCE PIC S9(9)V99 COMP-3.
05 FILLER PIC X(58).
2.3.2 工作存储节 (WORKING-STORAGE SECTION)
这是程序中大部分变量、常量和临时存储区域的定义位置。它的内容在程序执行开始时被初始化,并在程序执行期间一直存在。
-
级号 (Level Numbers):
-
01:最高级别的组项或独立项。一个01级数据项可以包含其他数据项(成为组项),也可以是独立的(成为基本项)。 -
02到49:用于定义组项内的子项。数字越大,表示层级越深,通常以 05, 10, 15 等递增。 -
77:用于定义独立的、非组项的基本数据项。它们不能包含其他数据项,也不能被其他数据项包含。 -
88:条件名 (Condition Name)。它不定义数据存储,而是定义一个布尔条件,当关联的数据项取特定值时,该条件为真。这极大地提高了代码的可读性。
-
WORKING-STORAGE SECTION.
01 WS-PROGRAM-FLAGS.
05 WS-END-OF-FILE-FLAG PIC X(1) VALUE 'N'.
88 END-OF-FILE-REACHED VALUE 'Y'.
88 NOT-END-OF-FILE VALUE 'N'.
77 WS-TOTAL-AMOUNT PIC S9(13)V99 COMP-3 VALUE ZEROS.
77 WS-ITEM-PRICE PIC 9(5)V92.
01 WS-CUSTOMER-DATA.
05 WS-CUST-ID PIC 9(8).
05 WS-CUST-NAME.
10 WS-CUST-LAST-NAME PIC X(20).
10 WS-CUST-FIRST-NAME PIC X(15).
05 WS-CUST-ADDRESS PIC X(40).
-
PICTURE 子句 (PIC):
这是 COBOL 中定义数据项格式的核心。它使用特定的字符组合来描述数据项的类型、大小和格式。
-
9:表示一个数字位。9(5)表示五位数字。 -
S:表示符号位。通常放在数字的最前面,如S9(5)表示一个带符号的五位数字。 -
V:表示隐含的小数点位置。它不占用存储空间。9(3)V99表示三位整数和两位小数。 -
P:表示隐含的十进制位置。用于非常大或非常小的数字,不占用存储空间,但表示了小数点相对于V的位置。 -
X:表示任何字符(字母、数字、特殊字符)。X(10)表示十个字符。 -
A:表示字母或空格。A(5)表示五个字母或空格。 -
编辑字符 (Editing Characters):用于在显示或打印时格式化数据。它们不能用于计算。
-
Z:前导零抑制。PIC Z(5)9会将00123显示为123。 -
*:星号填充。PIC **999会将12显示为**12,常用于支票打印防止篡改。 -
$:浮动美元符号。PIC $$$$.99会将12.34显示为$12.34。 -
+/-:浮动正负号。 -
,:千位分隔符。 -
.:显式小数点。 -
0:零插入。PIC 9900会将12显示为1200。 -
B:插入空格。PIC X(2)BX(2)会将ABCD显示为AB CD。 -
/:插入斜杠。PIC 99/99/99会将123199显示为12/31/99。
-
-
-
USAGE 子句:
指定数据项在计算机内存中的存储格式。这对于性能和与外部系统的数据交换至关重要。
-
DISPLAY(默认):以字符形式存储,每个字符占用一个字节(EBCDIC 或 ASCII)。适用于字符串和需要直接人机读取的数字。 -
COMPUTATIONAL(COMP):二进制形式存储,通常是全字或半字。适用于整数计算,但需要注意大小端和对齐问题。不同系统可能实现不同。 -
COMPUTATIONAL-3(COMP-3):Packed Decimal 格式。每个字节存储两个数字(半字节),除了最后一个字节存储一个数字和一个符号位。例如,数字 12345 存储为x'12345C'(C 表示正)。这是 COBOL 中最常用于商业计算的数字格式,因为它能精确表示十进制数,避免了浮点数精度问题,并且存储效率高于 DISPLAY 数字。 -
COMPUTATIONAL-5(COMP-5):与COMP类似,但更明确地表示二进制存储,通常与 C 语言的int类型兼容。 -
COMPUTATIONAL-X(COMP-X):通常用于定义指针。 -
INDEX:用于PERFORM VARYING和SEARCH语句中的索引。 -
POINTER:用于存储内存地址。
-
-
VALUE 子句:
在程序开始执行时,为数据项指定初始值。
-
REDEFINES 子句:
允许一个数据项在内存中占据与另一个数据项相同的存储区域,但以不同的格式或结构进行解释。这对于在同一块内存中处理不同类型的数据非常有用。
01 WS-DATE-FIELD.
05 WS-DATE-YYYYMMDD PIC 9(8).
05 WS-DATE-MMDDYY REDEFINES WS-DATE-YYYYMMDD.
10 WS-DATE-MM PIC 9(2).
10 WS-DATE-DD PIC 9(2).
10 WS-DATE-YY PIC 9(4).
注意: 重新定义的数据项不能具有 OCCURS 子句,也不能重新定义一个具有 OCCURS 子句的数据项。
-
OCCURS 子句:
用于定义表格(数组)或重复的数据项。
-
OCCURS integer TIMES:定义一个固定大小的表格。 -
OCCURS integer-1 TO integer-2 TIMES DEPENDING ON data-name:定义一个变长表格,其大小由data-name的值决定。 -
INDEXED BY index-name:为表格定义一个索引,用于PERFORM VARYING和SEARCH语句。索引比下标(直接的数字位置)更有效率。 -
ASCENDING / DESCENDING KEY IS data-name:指定表格的排序键,与SEARCH ALL语句一起使用。
-
01 WS-ITEM-TABLE.
05 WS-ITEM OCCURS 10 TIMES INDEXED BY ITEM-IDX.
10 WS-ITEM-CODE PIC X(5).
10 WS-ITEM-DESC PIC X(20).
10 WS-ITEM-PRICE PIC 9(5)V99.
-
SYNCHRONIZED (SYNC):
用于告诉编译器将数据项对齐到特定的存储边界(例如字边界),以提高访问效率。通常用于 COMP 或 COMP-5 数据项。
-
JUSTIFIED (JUST RIGHT/LEFT):
通常用于 X 或 A 类型的字符数据项。当数据移入一个 JUSTIFIED 数据项时,它会右对齐或左对齐,并用空格填充。默认是左对齐。
2.3.3 本地存储节 (LOCAL-STORAGE SECTION) (COBOL 2002+)
这个节用于在子程序中定义局部变量。这些变量在每次调用子程序时都会被重新初始化,并且在子程序返回后会被释放。这使得子程序更具重入性。
2.3.4 连接节 (LINKAGE SECTION)
这个节用于定义子程序之间传递参数的数据项。当一个程序 CALL 另一个程序时,CALL 语句中 USING 子句指定的数据项必须与被调用程序的 LINKAGE SECTION 中的数据项一一对应。LINKAGE SECTION 中的数据项本身不分配存储空间,它们只是描述了被调用程序可以访问的外部存储区域。
LINKAGE SECTION.
01 LS-CUSTOMER-ID PIC X(10).
01 LS-CUSTOMER-NAME-OUT PIC X(30).
01 LS-RETURN-CODE PIC 9(1).
2.4 过程部 (PROCEDURE DIVISION)
这是 COBOL 程序的执行逻辑部分,包含了所有的处理指令。它由一系列的节 (SECTION) 和段 (PARAGRAPH) 组成,这些节和段包含了 COBOL 动词 (verbs) 和语句。
-
节 (SECTION):
是一个或多个段的逻辑分组。它们通常用于将程序划分为大的功能块。节名后面必须跟 SECTION 关键字和句号。执行流可以通过 PERFORM section-name 来跳转到某个节。
-
段 (PARAGRAPH):
是 COBOL 程序中执行逻辑的最小命名单元。段名后面紧跟着句号。执行流可以通过 PERFORM paragraph-name 或 GO TO paragraph-name 来跳转到某个段。
-
语句 (Statements):
由 COBOL 动词及其操作数组成,以句号结束。
所有 COBOL 程序至少包含一个 PROCEDURE DIVISION。程序执行从 PROCEDURE DIVISION 的第一个语句开始,顺序执行,直到遇到 GOBACK、STOP RUN 或程序结束。
2.4.1 COBOL 动词 (Verbs) 详解
COBOL 拥有丰富的动词集,用于执行各种数据处理、控制流、输入/输出和通信任务。
-
数据操作动词
-
MOVE:将数据从一个数据项移动到另一个数据项。-
MOVE source TO destination:最基本的形式。 -
MOVE CORRESPONDING group-source TO group-destination:将group-source中与group-destination中名称相同的子项(基本项)移动到对应的目标项。 -
注意:
MOVE语句会根据目标数据项的 PICTURE 和 USAGE 进行自动转换、截断或填充。例如,将数字移动到字符项会自动转换为字符,将字符串移动到较小的字符串会截断。
MOVE "ABC" TO WS-FIELD-X. -- WS-FIELD-X PIC X(5) will become "ABC " MOVE 123.45 TO WS-FIELD-NUM. -- WS-FIELD-NUM PIC S9(3)V99 COMP-3 MOVE WS-SOURCE-REC TO WS-DEST-REC. MOVE CORRESPONDING WS-EMP-DETAILS TO WS-PRINT-REC. -
-
ADD:执行加法运算。-
ADD identifier-1 TO identifier-2 -
ADD identifier-1, identifier-2 GIVING identifier-3 -
ADD ALL identifier-1 FOR identifier-2(不常用) -
ON SIZE ERROR:当计算结果超出目标数据项范围时执行。 -
NOT ON SIZE ERROR:当计算结果未超出范围时执行。
ADD WS-AMOUNT-1 TO WS-TOTAL-AMOUNT. ADD WS-SALES-TAX, WS-PRODUCT-PRICE GIVING WS-TOTAL-BILL ROUNDED. ADD WS-COUNT-1, WS-COUNT-2, WS-COUNT-3 GIVING WS-GRAND-TOTAL ON SIZE ERROR DISPLAY "Error: Total exceeds capacity." NOT ON SIZE ERROR DISPLAY "Calculation successful." END-ADD. -
-
SUBTRACT:执行减法运算。-
SUBTRACT identifier-1 FROM identifier-2 -
SUBTRACT identifier-1 FROM identifier-2 GIVING identifier-3
SUBTRACT WS-PAYMENT FROM WS-BALANCE. SUBTRACT WS-DISCOUNT FROM WS-ORIGINAL-PRICE GIVING WS-NET-PRICE. -
-
MULTIPLY:执行乘法运算。-
MULTIPLY identifier-1 BY identifier-2 -
MULTIPLY identifier-1 BY identifier-2 GIVING identifier-3
MULTIPLY WS-QTY BY WS-PRICE. MULTIPLY WS-RATE BY WS-PRINCIPAL GIVING WS-INTEREST ROUNDED. -
-
DIVIDE:执行除法运算。-
DIVIDE identifier-1 INTO identifier-2 -
DIVIDE identifier-1 INTO identifier-2 GIVING identifier-3 REMAINDER identifier-4 -
DIVIDE identifier-1 BY identifier-2 GIVING identifier-3(COBOL 2002+)
DIVIDE WS-DENOMINATOR INTO WS-NUMERATOR. DIVIDE WS-TOTAL-STUDENTS INTO WS-TOTAL-SCORE GIVING WS-AVERAGE. DIVIDE WS-TOTAL-ITEMS BY WS-ITEMS-PER-BOX GIVING WS-NUM-BOXES REMAINDER WS-REMAINING-ITEMS. -
-
COMPUTE:允许使用算术表达式进行计算。更灵活,可以组合多种运算符。-
运算符:
+(加),-(减),*(乘),/(除),**(幂)。 -
运算符优先级:
**>*,/>+,-。可以使用括号改变优先级。
COMPUTE WS-AREA = WS-LENGTH * WS-WIDTH. COMPUTE WS-NET-PAY = WS-GROSS-PAY - (WS-TAX + WS-DEDUCTIONS). COMPUTE WS-RESULT ROUNDED = (WS-VALUE-1 + WS-VALUE-2) / WS-COUNT. ```ROUNDED`:四舍五入到目标数据项的小数位数。 -
-
-
控制流动词
-
PERFORM:执行一段代码(段或节)或一组语句。这是 COBOL 中实现结构化编程的关键。-
PERFORM paragraph-name:执行一次指定段。 -
PERFORM section-name:执行一次指定节。 -
PERFORM paragraph-name THRU paragraph-name-end:执行一个段范围内的所有段。 -
PERFORM paragraph-name TIMES identifier/literal:执行指定段多次。 -
PERFORM paragraph-name UNTIL condition:重复执行直到条件为真。 -
PERFORM VARYING index-name FROM initial-value BY increment UNTIL condition:循环迭代,常用于遍历表格。 -
内联
PERFORM(COBOL 85+):PERFORM UNTIL END-OF-FILE-REACHED READ INPUT-FILE NEXT RECORD AT END SET END-OF-FILE-REACHED TO TRUE END-READ IF NOT END-OF-FILE-REACHED PROCESS-RECORD END-IF END-PERFORM. -
PERFORM WITH TEST BEFORE/AFTER:指定条件测试的时间。默认是TEST BEFORE。 -
PERFORM动词允许将程序分解为可重用的模块,大大提高了可读性和可维护性,避免了过多的GO TO。
-
-
GO TO:无条件跳转到指定的段或节。虽然 COBOL 支持GO TO,但在现代编程实践中,应尽量避免使用它,因为滥用GO TO会导致“意大利面条式代码”,难以阅读和维护。结构化编程的原则是尽量减少GO TO的使用。IF WS-ERROR-FLAG = 'Y' GO TO ERROR-ROUTINE. -
IF:条件判断语句。-
IF condition THEN statement-1 ELSE statement-2 END-IF. -
NEXT SENTENCE:执行下一个语句(跳过当前语句的其余部分)。 -
嵌套
IF:
IF WS-AGE > 18 IF WS-HAS-LICENSE = 'Y' DISPLAY "Eligible to drive." ELSE DISPLAY "Not eligible to drive, no license." END-IF ELSE DISPLAY "Not eligible to drive, too young." END-IF.-
ELSE和END-IF关键字使得IF语句的结构更清晰。
-
-
EVALUATE(COBOL 85+):多路分支选择语句,类似于switch-case。比嵌套IF语句更清晰。EVALUATE WS-TRANSACTION-CODE WHEN "PUR" PERFORM PROCESS-PURCHASE. WHEN "RET" PERFORM PROCESS-RETURN. WHEN "REF" ALSO "ADJ" PERFORM PROCESS-REFUND-ADJUSTMENT. WHEN OTHER DISPLAY "Invalid Transaction Code: " WS-TRANSACTION-CODE. END-EVALUATE.-
WHEN ANY:匹配任何值。 -
WHEN TRUE:当条件为真时执行。
-
-
-
输入/输出动词
-
OPEN:打开文件,使其可用于输入或输出操作。-
OPEN INPUT file-name:以输入模式打开文件。 -
OPEN OUTPUT file-name:以输出模式打开文件(如果文件存在,则清空;如果不存在,则创建)。 -
OPEN I-O file-name:以输入/输出模式打开文件,可读可写(通常用于索引文件和相对文件)。 -
OPEN EXTEND file-name:以追加模式打开文件,在文件末尾写入。
OPEN INPUT CUSTOMER-FILE. OPEN OUTPUT REPORT-FILE. -
-
CLOSE:关闭文件,释放文件资源。-
CLOSE file-name-1 [file-name-2 ...]
CLOSE CUSTOMER-FILE. CLOSE REPORT-FILE. -
-
READ:从文件中读取一条记录到文件记录区。-
READ file-name INTO data-area AT END imperative-statement:当读取到文件末尾时执行AT END后面的语句。 -
READ file-name NEXT RECORD:用于动态访问模式,顺序读取下一条记录。 -
READ file-name WITH NO LOCK:读取时不上锁(并发控制)。 -
INVALID KEY:对于索引文件或相对文件,当找不到指定键的记录时触发。
READ CUSTOMER-FILE INTO WS-CUSTOMER-RECORD AT END SET END-OF-CUSTOMER-FILE TO TRUE NOT AT END ADD 1 TO WS-CUSTOMER-COUNT END-READ. -
-
WRITE:将文件记录区的内容写入文件。-
WRITE record-name [FROM data-area]:将record-name的内容写入文件。如果使用FROM data-area,则先将data-area的内容移动到record-name,然后再写入。 -
AFTER ADVANCING integer LINES:在打印机输出中,写入后跳过指定行数。 -
AFTER ADVANCING mnemonic-name:使用SPECIAL-NAMES中定义的助记符进行高级打印控制(如跳页)。
WRITE CUSTOMER-RECORD FROM WS-CUSTOMER-RECORD. WRITE PRINT-LINE FROM WS-DETAIL-LINE AFTER ADVANCING 1 LINE. WRITE PRINT-LINE FROM WS-HEADER-LINE AFTER ADVANCING NEW-PAGE. -
-
ACCEPT:从标准输入设备(通常是键盘或批处理作业输入流)接收数据。-
ACCEPT data-name [FROM mnemonic-name]
ACCEPT WS-USER-INPUT. ACCEPT WS-CURRENT-DATE FROM DATE. ACCEPT WS-CURRENT-TIME FROM TIME. -
-
DISPLAY:向标准输出设备(通常是屏幕或批处理作业日志)显示数据。-
DISPLAY data-name-1 [data-name-2 ...] [UPON mnemonic-name]
DISPLAY "Program started.". DISPLAY WS-TOTAL-SALES UPON CONSOLE. -
-
-
程序间通信动词
-
CALL:调用另一个程序(子程序)。-
CALL "program-id" [USING data-item-1 [data-item-2 ...]]: -
BY REFERENCE(默认):传递数据项的地址,被调用程序可以直接修改原始数据。 -
BY CONTENT:传递数据项的值(副本),被调用程序不能修改原始数据。 -
BY VALUE(COBOL 2002+):传递数据项的值,通常用于与 C/Java 等语言交互。
CALL "VALIDATE-CUSTOMER-ID" USING WS-CUSTOMER-ID, WS-VALID-FLAG. CALL "CALCULATE-TAX" USING BY CONTENT WS-GROSS-AMOUNT BY REFERENCE WS-TAX-AMOUNT. -
-
ENTRY:定义子程序的备用入口点。不常用。 -
EXIT PROGRAM:从子程序返回到调用程序。
-
-
表格处理动词
-
SET:-
用于设置索引 (INDEX) 的值。
-
用于设置条件名 (88 级数据项) 为真或假。
SET ITEM-IDX TO 1. SET END-OF-FILE-REACHED TO TRUE. SET NOT-END-OF-FILE TO TRUE. -
-
SEARCH:对表格进行线性搜索。-
SEARCH data-name VARYING index-name AT END imperative-statement WHEN condition imperative-statement -
SEARCH从当前索引位置开始搜索,直到找到满足条件的项或到达表格末尾。
SEARCH WS-ITEM VARYING ITEM-IDX AT END DISPLAY "Item not found." WHEN WS-ITEM-CODE(ITEM-IDX) = WS-SEARCH-CODE DISPLAY "Item found at index " ITEM-IDX DISPLAY "Item description: " WS-ITEM-DESC(ITEM-IDX) END-SEARCH. -
-
SEARCH ALL:对表格进行二分查找。要求表格必须已按ASCENDING/DESCENDING KEY排序。-
SEARCH ALL data-name AT END imperative-statement WHEN condition imperative-statement
SEARCH ALL WS-ITEM AT END DISPLAY "Item not found by SEARCH ALL." WHEN WS-ITEM-CODE(ITEM-IDX) = WS-SEARCH-CODE DISPLAY "Item found by SEARCH ALL at index " ITEM-IDX END-SEARCH. -
-
-
字符串操作动词
-
STRING:将多个字符串连接到一个目标字符串中,并提供填充和截断控制。-
STRING data-item-1 DELIMITED BY SIZE/data-item-2 ... INTO data-item-target
STRING WS-FIRST-NAME DELIMITED BY SIZE SPACE DELIMITED BY SIZE WS-LAST-NAME DELIMITED BY SIZE INTO WS-FULL-NAME. -
-
UNSTRING:将一个字符串根据分隔符分解成多个子字符串。-
UNSTRING source-string DELIMITED BY delimiter INTO target-1 [target-2 ...]
UNSTRING WS-FULL-ADDRESS DELIMITED BY "," OR SPACE INTO WS-STREET, WS-CITY, WS-STATE, WS-ZIP. -
-
INSPECT:检查字符串内容并执行替换、计数或转换操作。-
INSPECT data-name TALLYING count-data-item FOR ALL/LEADING/CHARACTERS 'string' -
INSPECT data-name REPLACING ALL/LEADING/FIRST 'string-1' BY 'string-2' -
INSPECT data-name CONVERTING from-string TO to-string
INSPECT WS-STRING TALLYING WS-SPACE-COUNT FOR ALL SPACES. INSPECT WS-PHONE-NUMBER REPLACING ALL "-" BY "". INSPECT WS-TEXT CONVERTING "abcdef" TO "ABCDEF". -
-
-
排序和合并动词
-
SORT:对文件或表格中的记录进行排序。-
SORT file-name ON ASCENDING/DESCENDING KEY key-field-1 ... USING input-file-name GIVING output-file-name -
SORT file-name ON ... INPUT PROCEDURE section-name OUTPUT PROCEDURE section-name -
INPUT PROCEDURE和OUTPUT PROCEDURE允许在排序前对数据进行预处理,在排序后进行后处理。
SORT SORT-WORK-FILE ON ASCENDING KEY WS-CUST-ID USING CUSTOMER-INPUT-FILE GIVING SORTED-CUSTOMER-FILE. -
-
MERGE:合并两个或多个已排序的文件为一个已排序的文件。-
MERGE file-name ON ASCENDING/DESCENDING KEY key-field-1 ... USING input-file-name-1, input-file-name-2 ... GIVING output-file-name
MERGE MERGE-WORK-FILE ON ASCENDING KEY WS-ORDER-DATE USING ORDER-FILE-1, ORDER-FILE-2 GIVING COMBINED-ORDER-FILE. -
-
-
其他动词
-
STOP RUN:终止程序的执行,并将控制返回给操作系统。 -
GOBACK:终止当前程序的执行,并将控制返回给调用程序(如果是子程序)或操作系统(如果是主程序)。在大型机环境下,GOBACK是更常见的终止程序的方式,因为它既可以用于主程序也可以用于子程序。 -
EXIT:在段或节的末尾作为逻辑出口点。通常与PERFORM循环一起使用,表示循环体的结束。 -
CONTINUE:无操作语句,用于语法上需要一个语句但逻辑上不需要任何操作的情况。 -
INITIALIZE:将数据项初始化为其默认值(数字为零,字符为空格)。
INITIALIZE WS-CUSTOMER-RECORD. INITIALIZE WS-WORK-AREAS.-
INSPECT:检查字符串内容,计数特定字符或替换字符。
-
第三章:COBOL的数据处理与存储
COBOL 对数据处理的细致控制是其在商业领域成功的关键之一。其强大的 PICTURE 子句和 USAGE 子句允许程序员精确地定义数据的格式和在内存中的存储方式,这对于处理金融数据、确保精度和优化存储效率至关重要。
3.1 PICTURE 子句的深入理解
除了前面介绍的基本字符,PICTURE 子句还有更多高级用法和细节。
-
组合字符与重复计数:
-
X(n):重复n次。 -
9(n):重复n次。 -
Z(n):重复n次前导零抑制。 -
$(n):重复n次浮动美元符号。
-
-
符号表示 (S):
S 只能出现在数字型 PICTURE 字符串的最左边。它表示该数字是带符号的。在 DISPLAY 格式中,符号通常不显示,但在内部计算时会被考虑。如果需要显示符号,可以使用编辑型的 PICTURE,如 +999.99 或 -999.99。
-
S9(5):五位数字,带符号。
-
-
隐含小数点 (V):
V 表示数字内部小数点的位置。它不占用存储空间,只在执行算术运算时由编译器处理。
-
99V99:表示两位整数和两位小数。例如,值为 1234 的PIC 99V99数据项表示 12.34。
-
-
显示小数点 (.):
. 表示实际的十进制小数点字符,它会占用一个字符的存储空间,并用于显示。
-
99.99:表示两位整数,一个小数点,两位小数。
-
-
零填充 (P):
P 表示数字中隐含的零。通常用于非常大或非常小的数字,以节省存储空间。
-
99P(3):表示实际数字是99000。 -
P(3)99:表示实际数字是.00099。
-
-
编辑型 PICTURE:
用于将数据格式化为可读的输出形式。编辑字符会占用存储空间。
-
浮动美元符号 (
$) 和前导零抑制 (Z):-
PIC $$,$$9.99:将1234.56显示为$1,234.56。 -
PIC ZZZ,ZZ9.99:将000123.45显示为123.45。
-
-
插入字符 (
B,0,/,,,+,-,CR,DB):-
B(Blank):插入空格。PIC 99B99将1234格式化为12 34。 -
0(Zero):插入零。PIC 9900将12格式化为1200。 -
/(Slash):插入斜杠。PIC 99/99/99将123199格式化为12/31/99。 -
,(Comma):插入逗号作为千位分隔符。PIC 9,999将1234格式化为1,234。 -
+,-,CR(Credit),DB(Debit):用于显示数字的符号或借贷状态。CR和DB通常出现在数字的右侧。-
PIC +999:+123或-123 -
PIC 999.99CR:123.45CR(负数) 或123.45(正数)
-
-
-
3.2 USAGE 子句的内部表示
USAGE 子句决定了数据在内存中的实际二进制表示。这对于与硬件架构、操作系统以及其他语言(如汇编或 C)的接口至关重要。
-
DISPLAY:-
存储:每个字符占用一个字节。数字以字符形式存储(EBCDIC 或 ASCII)。
-
优点:人机可读,易于调试。
-
缺点:存储效率低,进行算术运算时需要内部转换,效率较慢。
-
示例:
PIC 9(5)存储12345将占用 5 个字节。PIC X(5)存储HELLO将占用 5 个字节。 -
带符号的 DISPLAY 数字:符号通常存储在数字的最后一个半字节中(Overpunch),例如,
PIC S9(3) DISPLAY存储123可能在内存中表示为x'F1F2C3'(IBM EBCDIC)。
-
-
COMPUTATIONAL(COMP 或 BINARY):-
存储:以二进制整数形式存储。通常使用 2 字节(半字)、4 字节(全字)或 8 字节(双字)进行存储,具体取决于
PICTURE子句的位数。 -
优点:算术运算速度快,存储效率高。
-
缺点:精度有限(浮点数问题),与硬件对齐相关,不直接人机可读。
-
位数与字节映射 (典型):
-
PIC S9(1)到S9(4):通常 2 字节 (半字) -
PIC S9(5)到S9(9):通常 4 字节 (全字) -
PIC S9(10)到S9(18):通常 8 字节 (双字)
-
-
示例:
PIC S9(9) COMP存储123456789将占用 4 个字节,以二进制形式存储。
-
-
COMPUTATIONAL-3(COMP-3 或 PACKED-DECIMAL):-
存储:压缩十进制 (Packed Decimal) 格式。每个字节存储两个十进制数字,最后一个字节存储一个数字和一个符号位。
-
存储计算:
CEIL((位数 + 1) / 2)字节。例如,PIC S9(5)V99有 7 位数字,存储需要CEIL((7+1)/2) = 4字节。 -
优点:精确表示十进制数,避免了浮点数运算的精度问题,存储效率高于 DISPLAY。是商业计算中的标准。
-
缺点:在非十进制硬件上运算速度略慢于二进制,不直接人机可读。
-
示例:
PIC S9(5) COMP-3存储12345。-
12 34 5C(C表示正数,F表示负数,D表示无符号正数) -
占用 3 个字节。
-
-
-
COMPUTATIONAL-5(COMP-5):-
存储:以标准二进制整数格式存储,通常与 C 语言的
int,long等兼容。它确保了跨平台的一致性。 -
优点:跨平台兼容性好,与 C 等语言的接口方便。
-
缺点:与
COMP类似,有精度限制。 -
示例:
PIC S9(9) COMP-5存储123456789将占用 4 个字节,行为更像标准的 C int。
-
-
INDEX:-
存储:存储一个指针,用于快速访问表格元素。通常是 4 字节或 8 字节。
-
优点:比使用下标更快,因为它直接存储内存地址偏移。
-
用途:用于
PERFORM VARYING和SEARCH语句。
-
3.3 数据层次与组项
COBOL 数据的核心概念是层次结构。通过使用不同级别的级号 (level numbers),可以将相关的数据项分组在一起,形成组项。
-
组项 (Group Item):
包含一个或多个较低级数据项的数据项。对组项的操作会影响其所有子项。例如,MOVE group-item-A TO group-item-B 会将 group-item-A 的所有内容按字符移动到 group-item-B。
-
基本项 (Elementary Item):
不包含任何较低级数据项的数据项。所有 PICTURE 子句都只能定义在基本项上。
-
FILLER 数据项:
用于填充组项中的空白区域,或者作为对程序逻辑不重要的占位符。FILLER 数据项不能被引用。
01 CUSTOMER-RECORD. 05 CUSTOMER-ID PIC X(10). 05 CUSTOMER-NAME PIC X(30). 05 FILLER PIC X(58). -- 填充剩余的记录空间在现代 COBOL 中,
FILLER关键字通常可以省略,只留下级号和 PICTURE。
3.4 条件名 (Condition Names) - 88 级
条件名 (88 级数据项) 是 COBOL 中一个非常独特的特性,它极大地增强了代码的可读性。它不是一个存储数据的变量,而是与一个基本数据项关联的布尔条件。当基本数据项的值与条件名定义的值匹配时,该条件名为真。
01 TRANSACTION-STATUS PIC X(1).
88 STATUS-NEW VALUE 'N'.
88 STATUS-PROCESSING VALUE 'P'.
88 STATUS-COMPLETED VALUE 'C'.
88 STATUS-ERROR VALUE 'E'.
88 VALID-STATUS VALUES ARE 'N', 'P', 'C', 'E'.
在 PROCEDURE DIVISION 中:
IF STATUS-NEW
PERFORM INITIALIZE-TRANSACTION.
ELSE IF STATUS-COMPLETED
PERFORM ARCHIVE-TRANSACTION.
ELSE IF VALID-STATUS
DISPLAY "Processing transaction...".
ELSE
DISPLAY "Invalid transaction status: " TRANSACTION-STATUS.
END-IF.
使用条件名可以使 IF 语句的条件表达更加自然和易懂,减少了硬编码的字面量比较。
第四章:文件处理与管理
COBOL 在文件处理方面异常强大和灵活,支持多种文件组织方式和访问模式,这正是它在商业数据处理中不可或缺的原因。
4.1 文件组织 (ORGANIZATION)
-
SEQUENTIAL(顺序文件):-
特点:记录按物理顺序存储,通常是记录被写入的顺序。访问只能从头到尾顺序进行。
-
用途:最简单和最常见的文件类型,适用于日志文件、批处理输入/输出、报告文件等。
-
操作:
OPEN INPUT/OUTPUT/EXTEND,READ NEXT RECORD,WRITE。 -
限制:无法随机访问特定记录。
-
-
INDEXED(索引文件):-
特点:记录通过一个或多个键进行组织。系统维护一个或多个索引,将键值映射到记录的物理位置。
-
用途:用于需要快速随机访问特定记录的场景,如客户查询、库存查询等。也支持顺序访问。
-
键:
-
主键 (
RECORD KEY):必须唯一。 -
备用键 (
ALTERNATE RECORD KEY):可以不唯一 (WITH DUPLICATES)。
-
-
操作:
-
OPEN INPUT/OUTPUT/I-O/EXTEND。 -
随机访问:将键值放入对应的键数据项,然后
READ。 -
顺序访问:
READ NEXT RECORD。 -
WRITE:写入新记录(键值不能重复)。 -
REWRITE:更新现有记录(必须先READ)。 -
DELETE:删除记录(必须先READ)。
-
-
FILE STATUS:对于索引文件,FILE STATUS代码更加重要,例如 '23' (记录未找到), '22' (键重复), '21' (文件未找到)。
-
-
RELATIVE(相对文件):-
特点:记录通过其相对记录号(从 1 开始的整数)进行组织。可以随机访问特定记录,通过计算其在文件中的相对位置。
-
用途:适用于记录大小固定且可以方便地映射到相对位置的场景,例如,一个简单的学生ID映射。
-
RELATIVE KEY:一个数据项,用于存储或指定记录的相对记录号。 -
操作:
-
OPEN INPUT/OUTPUT/I-O/EXTEND。 -
随机访问:将相对记录号放入
RELATIVE KEY数据项,然后READ。 -
顺序访问:
READ NEXT RECORD。 -
WRITE:写入新记录。 -
REWRITE:更新现有记录。 -
DELETE:删除记录。
-
-
4.2 文件状态 (FILE STATUS)
FILE STATUS 子句在 FILE-CONTROL 段中定义,将一个两字符的数据项与每个文件关联。每次文件操作(OPEN, CLOSE, READ, WRITE, REWRITE, DELETE)完成后,COBOL 运行时会将一个状态码放入这个数据项中。这是 COBOL 程序中进行文件错误处理和逻辑判断的标准且强烈推荐的方式。
-
常见状态码:
-
'00':成功完成。 -
'10':文件末尾 (End-of-File)。仅用于READ操作。 -
'21':文件不存在或无法打开(例如,在OPEN INPUT时)。 -
'22':键重复(在索引文件写入时,主键或带有NO DUPLICATES的备用键已存在)。 -
'23':记录未找到(在索引或相对文件读取时,指定键的记录不存在)。 -
'30':永久性错误(例如,磁盘故障)。 -
'9x':制造商定义的状态码,通常表示其他系统级错误。
-
最佳实践:在每次文件操作后,都应该检查 FILE STATUS 数据项的值,并根据不同的状态码执行相应的错误处理或逻辑分支。
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT CUSTOMER-MASTER ASSIGN TO 'CUSTMST'
ORGANIZATION IS INDEXED
ACCESS MODE IS RANDOM
RECORD KEY IS CM-CUSTOMER-ID
FILE STATUS IS WS-CUSTOMER-STATUS.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CUSTOMER-STATUS PIC X(2).
88 CUSTOMER-STATUS-OK VALUE '00'.
88 CUSTOMER-STATUS-EOF VALUE '10'.
88 CUSTOMER-STATUS-NOT-FOUND VALUE '23'.
88 CUSTOMER-STATUS-DUPLICATE VALUE '22'.
88 CUSTOMER-STATUS-ERROR VALUES ARE '21', '30', '90'.
PROCEDURE DIVISION.
MAIN-ROUTINE.
OPEN INPUT CUSTOMER-MASTER.
IF NOT CUSTOMER-STATUS-OK
DISPLAY "Error opening customer master file: " WS-CUSTOMER-STATUS
GOBACK
END-IF.
MOVE "CUST001" TO CM-CUSTOMER-ID.
READ CUSTOMER-MASTER.
EVALUATE WS-CUSTOMER-STATUS
WHEN CUSTOMER-STATUS-OK
DISPLAY "Customer found: " CM-CUSTOMER-NAME
WHEN CUSTOMER-STATUS-NOT-FOUND
DISPLAY "Customer CUST001 not found."
WHEN OTHER
DISPLAY "Error reading customer master file: " WS-CUSTOMER-STATUS
END-EVALUATE.
CLOSE CUSTOMER-MASTER.
GOBACK.
第五章:高级特性与扩展
COBOL 在其漫长的发展过程中,也吸纳了一些高级特性和扩展,以适应不断变化的编程范式和系统需求。
5.1 子程序 (Subprograms)
子程序是 COBOL 中实现模块化和代码重用的基本机制。一个 COBOL 程序可以调用另一个 COBOL 程序,也可以调用用其他语言(如 C、汇编)编写的子程序。
-
调用方式:
-
静态调用 (Static Call):被调用的子程序在编译时与主程序链接在一起,形成一个单独的可执行模块。这通常速度更快,但更占用内存。
-
动态调用 (Dynamic Call):被调用的子程序在运行时被加载到内存中。这更灵活,因为子程序可以在需要时才加载,且可以动态地替换。
-
-
参数传递:
CALL 语句的 USING 子句用于传递参数,这些参数在被调用程序的 LINKAGE SECTION 中定义。
-
BY REFERENCE(默认):传递数据项的内存地址。被调用程序可以直接修改原始数据。这是最常见的方式。 -
BY CONTENT:传递数据项的值的副本。被调用程序无法修改原始数据,只能读取。 -
BY VALUE(COBOL 2002+):通常用于与 C/Java 等外部语言的互操作,按值传递。
-
IDENTIFICATION DIVISION.
PROGRAM-ID. MAINPROG.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TOTAL PIC 9(5) VALUE 100.
01 WS-FACTOR PIC 9(2) VALUE 2.
01 WS-RESULT PIC 9(5).
PROCEDURE DIVISION.
MAIN-PARA.
DISPLAY "Before call: WS-TOTAL = " WS-TOTAL ", WS-FACTOR = " WS-FACTOR.
CALL "SUBPROG" USING WS-TOTAL, BY CONTENT WS-FACTOR, WS-RESULT.
DISPLAY "After call: WS-TOTAL = " WS-TOTAL ", WS-RESULT = " WS-RESULT.
GOBACK.
IDENTIFICATION DIVISION.
PROGRAM-ID. SUBPROG.
DATA DIVISION.
LINKAGE SECTION.
01 LS-TOTAL PIC 9(5).
01 LS-FACTOR PIC 9(2).
01 LS-RESULT PIC 9(5).
PROCEDURE DIVISION USING LS-TOTAL, LS-FACTOR, LS-RESULT.
SUB-PARA.
ADD LS-FACTOR TO LS-TOTAL. -- LS-TOTAL会被修改
COMPUTE LS-RESULT = LS-TOTAL * LS-FACTOR.
DISPLAY "Inside SUBPROG: LS-TOTAL = " LS-TOTAL ", LS-FACTOR = " LS-FACTOR ", LS-RESULT = " LS-RESULT.
EXIT PROGRAM.
5.2 内联 PERFORM 和 IF 结构 (COBOL 85+)
COBOL 85 引入了许多旨在支持结构化编程的特性,其中最重要的就是范围终止符 (END-IF, END-PERFORM, END-READ 等) 和内联 PERFORM。这些特性使得代码更加清晰,减少了对段或节的依赖,避免了 GO TO。
-- 传统方式 (需要单独的段)
PERFORM PROCESS-DATA.
PROCESS-DATA.
READ INPUT-FILE AT END GO TO END-READ-PARA.
IF WS-FLAG = 'Y' GO TO PROCESS-FLAG-Y.
...
END-READ-PARA. EXIT.
PROCESS-FLAG-Y. ...
-- 现代内联方式 (更清晰)
PERFORM UNTIL WS-EOF-FLAG IS EQUAL TO 'Y'
READ INPUT-FILE
AT END SET WS-EOF-FLAG TO 'Y'
NOT AT END
IF WS-FLAG = 'Y'
DISPLAY "Flag is Y, processing..."
ELSE
DISPLAY "Flag is not Y."
END-IF
END-READ
END-PERFORM.
5.3 内部子程序 (Nested Programs) (COBOL 85+)
一个 COBOL 程序可以包含另一个 COBOL 程序作为其内部程序。内部程序可以访问外部程序的 DATA DIVISION 中的数据项,这提供了一种封装数据和逻辑的方式。
IDENTIFICATION DIVISION.
PROGRAM-ID. OUTER-PROG.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 OUTER-DATA PIC X(10) VALUE "OUTER".
PROCEDURE DIVISION.
OUTER-PARA.
DISPLAY "Outer Program - Before Call: " OUTER-DATA.
CALL "INNER-PROG".
DISPLAY "Outer Program - After Call: " OUTER-DATA.
GOBACK.
IDENTIFICATION DIVISION.
PROGRAM-ID. INNER-PROG.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 INNER-DATA PIC X(10) VALUE "INNER".
PROCEDURE DIVISION.
INNER-PARA.
DISPLAY "Inner Program - Before Modify: " OUTER-DATA.
MOVE "MODIFIED" TO OUTER-DATA. -- 内部程序可以修改外部程序的数据
DISPLAY "Inner Program - After Modify: " OUTER-DATA.
EXIT PROGRAM.
END PROGRAM INNER-PROG.
END PROGRAM OUTER-PROG.
5.4 固有函数 (Intrinsic Functions) (COBOL 85+)
COBOL 85 引入了固有函数,提供了执行常见操作(如数学计算、日期处理、字符串操作)的标准方法,提高了代码的可移植性和可读性。
-
数学函数:
SQRT,LOG,COS,SIN等。 -
日期/时间函数:
CURRENT-DATE,DATE-OF-INTEGER,INTEGER-OF-DATE,WHEN-COMPILED。 -
字符串函数:
LENGTH,UPPER-CASE,LOWER-CASE,REVERSE,NUMVAL,NUMVAL-C(带货币符号的数字字符串转换为数字)。 -
统计函数:
MEAN,MEDIAN,SUM,VARIANCE,STANDARD-DEVIATION。
COMPUTE WS-SQRT-VALUE = FUNCTION SQRT(WS-INPUT-NUMBER).
MOVE FUNCTION UPPER-CASE("hello world") TO WS-OUTPUT-STRING.
MOVE FUNCTION NUMVAL-C("$1,234.56") TO WS-AMOUNT.
DISPLAY FUNCTION CURRENT-DATE (1:8). -- 显示 YYYYMMDD
5.5 面向对象 COBOL (COBOL 2002+)
COBOL 2002 标准引入了完整的面向对象特性,试图将 COBOL 语言现代化。
-
类 (CLASS):通过
CLASS-ID声明。 -
对象 (OBJECT):类的实例。
-
方法 (METHOD):对象可以执行的操作。
-
属性 (PROPERTY):对象的特性。
-
继承 (INHERITANCE):从现有类派生新类。
-
多态 (POLYMORPHISM):不同对象响应相同消息的方式不同。
OO COBOL 的引入是为了让 COBOL 程序能够更好地适应现代软件工程实践,并与其他面向对象语言进行集成。然而,由于历史遗留系统大多是过程式,OO COBOL 的应用远不如过程式 COBOL 普及。
-- 概念性示例:COBOL 2002 中的类定义
CLASS-ID. CUSTOMER INHERITS FROM BASE-OBJECT.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS BASE-OBJECT AS "BaseObject".
DATA DIVISION.
WORKING-STORAGE SECTION.
01 CUSTOMER-ID PIC X(10).
01 CUSTOMER-NAME PIC X(30).
PROCEDURE DIVISION.
METHOD-ID. "GET-CUSTOMER-NAME".
DATA DIVISION.
LINKAGE SECTION.
01 LS-RETURN-NAME PIC X(30).
PROCEDURE DIVISION USING RETURNING LS-RETURN-NAME.
MOVE CUSTOMER-NAME TO LS-RETURN-NAME.
EXIT METHOD.
END METHOD "GET-CUSTOMER-NAME".
METHOD-ID. "SET-CUSTOMER-ID".
DATA DIVISION.
LINKAGE SECTION.
01 LS-NEW-ID PIC X(10).
PROCEDURE DIVISION USING LS-NEW-ID.
MOVE LS-NEW-ID TO CUSTOMER-ID.
EXIT METHOD.
END METHOD "SET-CUSTOMER-ID".
END CLASS CUSTOMER.
5.6 报告撰写器 (REPORT WRITER)
COBOL 提供了一个强大的 Report Writer 模块,用于简化复杂报告的生成。通过声明性的方式定义报告布局、页眉、页脚、控制中断和总计,Report Writer 可以自动处理分页、行计数、控制字段分组等任务。虽然现代报告工具更常用,但 Report Writer 在 COBOL 应用程序中仍然有其历史和实用价值。
-
REPORT SECTION:定义报告的结构。 -
RD (Report Description):描述报告的总体属性。 -
CONTROL IS:定义报告中的控制字段,当这些字段的值发生变化时,会触发控制中断(Control Break),用于分组数据和生成小计。 -
PAGE LIMIT IS:定义每页的最大行数。 -
TYPE:定义报告行的类型(如PAGE HEADING,DETAIL,CONTROL FOOTING,PAGE FOOTING,REPORT FOOTING)。 -
LINE:定义行的相对位置。 -
COLUMN:定义字段在行中的列位置。 -
SOURCE IS:指定数据项。 -
SUM:指定要累加的字段。
Report Writer Verb:
-
GENERATE:触发详细行或控制行的生成。 -
INITIATE:初始化 Report Writer。 -
TERMINATE:终止 Report Writer。
DATA DIVISION.
REPORT SECTION.
RD SALES-REPORT
CONTROLS ARE (SALES-REGION, SALES-DATE) -- 控制字段
PAGE LIMIT IS 60 LINES
HEADING 1
FIRST DETAIL 10
LAST DETAIL 50
FOOTING 55.
01 TYPE PAGE HEADING.
05 LINE 1.
10 COLUMN 1 PIC X(20) VALUE "SALES REPORT".
10 COLUMN 50 PIC X(10) SOURCE CURRENT-DATE.
01 TYPE CONTROL HEADING SALES-REGION.
05 LINE PLUS 1.
10 COLUMN 5 PIC X(15) VALUE "REGION:".
10 COLUMN 20 PIC X(10) SOURCE SALES-REGION.
01 TYPE DETAIL.
05 LINE PLUS 1.
10 COLUMN 10 PIC X(20) SOURCE CUSTOMER-NAME.
10 COLUMN 40 PIC 9(9).99 SOURCE SALES-AMOUNT.
01 TYPE CONTROL FOOTING SALES-REGION.
05 LINE PLUS 2.
10 COLUMN 30 PIC X(10) VALUE "REGION TOTAL:".
10 COLUMN 50 PIC 9(11).99 SUM SALES-AMOUNT.
PROCEDURE DIVISION.
MAIN-LOGIC.
INITIATE SALES-REPORT.
-- 读取数据,并在每次处理一条销售记录时
-- MOVE SALES-REGION TO REPORT-SALES-REGION (假设有此数据项)
-- MOVE SALES-AMOUNT TO REPORT-SALES-AMOUNT
-- GENERATE DETAIL. -- 触发报告行生成
TERMINATE SALES-REPORT.
GOBACK.
第六章:COBOL的优缺点与未来挑战
COBOL 是一把双刃剑,其设计哲学带来了无与伦比的稳定性和可靠性,但也伴随着一些局限性。
6.1 COBOL的优势
-
极高的可读性与自文档化:COBOL 的语法非常接近英语,使用大量动词和描述性短语,如
ADD A TO B GIVING C。这使得非程序员也能在一定程度上理解代码逻辑,并且非常易于维护(对于那些熟悉它的程序员)。 -
强大的文件处理能力:COBOL 天生为商业文件处理而设计,对顺序文件、索引文件、相对文件以及各种记录和字段的精细控制是其核心优势。
-
精确的十进制算术:通过
COMP-3(Packed Decimal) 和PICTURE子句,COBOL 能够进行精确的十进制算术运算,避免了浮点数在金融计算中可能引入的精度问题。这对于银行、保险等行业至关重要。 -
稳定性与可靠性:经过数十年的发展和在关键业务系统中的实际运行,COBOL 编译器和运行时环境非常成熟和稳定,能够处理海量的事务和数据,具有极高的容错性。
-
跨平台能力 (特定意义上):COBOL 程序代码在不同的 COBOL 编译器之间具有较高的可移植性,尽管需要修改
ENVIRONMENT DIVISION。 -
遗留系统的核心:全球数万亿的交易和绝大部分的业务逻辑仍然运行在 COBOL 代码上。这意味着掌握 COBOL 仍然是许多大型企业和机构的刚性需求。
-
人才稀缺性:虽然这是一个挑战,但对于掌握 COBOL 技能的程序员来说,也意味着就业机会和较高的薪资。
6.2 COBOL的局限性与挑战
-
冗长和繁琐:COBOL 的英语化语法虽然提高了可读性,但也使其非常冗长。一个简单的操作可能需要多行代码,这增加了代码量和开发时间。
-
缺乏现代编程范式:尽管 COBOL 2002 引入了面向对象特性,但主流的 COBOL 代码仍然是过程式的。它缺乏现代语言中常见的内置数据结构(如哈希表、列表)、高级算法库和函数式编程支持。
-
与现代开发工具集成有限:虽然有一些现代 IDE 支持 COBOL,但与 Java、Python 等语言的成熟生态系统相比,COBOL 在现代开发工具、框架、持续集成/持续部署 (CI/CD) 流程方面的集成度相对较低。
-
学习曲线与认知障碍:COBOL 的语法和编程哲学与现代语言截然不同,对于习惯了 C++、Java 或 Python 的新一代程序员来说,学习 COBOL 可能存在认知障碍。它的语法结构、数据定义方式和文件处理机制都需要重新适应。
-
人才老化:大部分掌握 COBOL 技能的程序员都接近退休年龄,而学习 COBOL 的新血相对不足,导致 COBOL 人才出现断层。
-
维护成本:庞大的 COBOL 代码库,有时缺乏良好的文档,使得维护和理解现有系统变得复杂和昂贵。
-
固定格式代码:传统的 COBOL 程序对代码格式有严格的规定(如列位置),这在现代自由格式的编程环境中显得格格不入。
6.3 COBOL的现代化与未来
为了应对这些挑战,COBOL 社区和相关厂商正在积极推动 COBOL 的现代化:
-
编译器和工具改进:GnuCOBOL (开源)、Micro Focus Enterprise Developer、IBM Z and Cloud Modernization Stack 等都提供了现代化的开发环境、调试工具和集成能力。
-
互操作性:通过 Web 服务 (SOAP/REST)、消息队列 (MQ)、Java Native Interface (JNI) 或 C/C++ 外部调用,COBOL 程序可以与基于 Java、.NET、Python 等技术栈的应用程序进行无缝集成。
-
云迁移:将大型机上的 COBOL 应用程序迁移到公共或私有云环境,利用容器化、微服务等技术。
-
培训与知识传承:一些大学和企业开始提供 COBOL 培训课程,以培养新一代 COBOL 开发者和维护者。
-
代码分析与自动化:利用自动化工具对现有 COBOL 代码进行分析、重构和文档生成,降低维护成本。
COBOL 的未来与其所服务的核心业务系统紧密相连。只要这些系统继续运行,COBOL 就有其存在的价值。它的演变更多地体现在与新技术的融合以及开发和维护工具的现代化上,而非其核心语言语法的剧烈变化。
第七章:关于“CobolScript”的探讨
如前所述,“CobolScript”并不是一个普遍存在的、独立的编程语言。这个术语可能引起联想,因为它结合了“COBOL”的特定领域(商业处理)和“Script”的动态、轻量级特性。然而,在大多数情况下,它指的是与 COBOL 程序相关的脚本化操作或集成方案,而非一门新的脚本语言。
我们将从几个方面探讨与“CobolScript”相关的概念:
7.1 大型机环境中的“脚本”:JCL
在大型机(如 IBM z/OS)环境中,JCL (Job Control Language) 是最接近“脚本”概念的存在。JCL 用于控制批处理作业的执行,包括:
-
指定要运行的程序:通常是编译后的 COBOL 可执行文件。
-
分配文件:将 COBOL 程序中
SELECT语句定义的文件名与实际的磁盘数据集 (DASD)、磁带或打印机输出关联。 -
传递运行时参数。
-
定义作业步骤的顺序和依赖关系。
-
处理条件分支和错误处理。
JCL 是一个高度专用的、强大的脚本语言,它是大型机批处理作业的“骨架”,COBOL 程序是其“血肉”。一个典型的 COBOL 批处理作业的执行流就是由 JCL 脚本来编排和管理的。
JCL 示例 (概念性):
//MYJOB JOB (ACCT,PROG), 'DAILY REPORT', CLASS=A, MSGCLASS=X
//STEP01 EXEC PGM=MYCOBOLPGM
//STEPLIB DD DSN=MY.COBOL.LOADLIB,DISP=SHR
//SYSIN DD DSN=MY.INPUT.DATA,DISP=SHR
//SYSOUT DD SYSOUT=*
//REPORT DD DSN=MY.DAILY.REPORT,DISP=(NEW,CATLG),
// DCB=(RECFM=FB,LRECL=132,BLKSIZE=0),
// SPACE=(TRK,(10,2),RLSE)
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
// PEND
这个 JCL 脚本指示系统执行名为 MYCOBOLPGM 的 COBOL 程序,并为其分配输入文件 (SYSIN)、输出报告文件 (REPORT) 等。因此,如果你在大型机上谈论 COBOL 的“脚本”,JCL 就是最重要的上下文。
7.2 现代脚本语言与COBOL的集成
随着开放系统和微服务架构的兴起,许多企业需要将他们的大型机 COBOL 系统与新的分布式应用程序进行集成。在这种场景下,“脚本化”可能意味着使用现代脚本语言(如 Python、Node.js、PowerShell 等)来:
-
调用 COBOL 服务:通过中间件(如 CICS Transaction Gateway、IBM MQ)或 Web 服务(通过 SOAP/REST API),脚本可以触发 COBOL 程序执行业务逻辑并获取结果。例如,一个 Python 脚本可以发送一个 REST 请求到 CICS 上的 COBOL 服务,以获取客户信息。
-
数据转换和迁移:脚本可以用于从大型机 COBOL 文件中提取数据,将其转换为现代格式(如 JSON、XML),然后加载到关系数据库或数据湖中。反之亦然。
-
自动化操作和部署:使用脚本自动化 COBOL 应用程序的构建、测试、部署和监控过程。例如,一个 Jenkins 管道可以使用 Groovy 脚本来调用大型机上的 COBOL 编译命令。
-
报告和分析:脚本可以从 COBOL 生成的平面文件中读取数据,然后使用数据分析库(如 Python 的 Pandas)进行处理和可视化。
这种集成方案通常不涉及用“CobolScript”编写代码,而是通过已有的 COBOL 程序暴露接口,让外部的脚本程序来调用和协调。
7.3 小众或概念性“CobolScript”项目
在少数情况下,可能会出现尝试将 COBOL 语法或理念与脚本语言特性结合的实验性项目。这些项目可能旨在:
-
提供更轻量级的 COBOL 开发和部署方式,无需完整的大型机环境。
-
将 COBOL 的数据处理能力暴露给更现代的脚本环境。
然而,这类项目通常规模较小,缺乏广泛的社区支持和工业应用。它们不太可能形成一个像 Python 或 JavaScript 那样成熟的“脚本语言”生态系统。例如,可能会有某个工具或框架提供一种类似 COBOL 的 DSL (领域特定语言) 来处理数据,但它通常运行在一个更通用的脚本语言(如 Java 或 Python)之上,而非独立的“CobolScript”虚拟机。
7.4 为什么没有一个主流的“CobolScript”语言?
-
COBOL 的设计理念:COBOL 的核心优势在于其严谨性、强类型和编译时检查。它是一种编译型语言,旨在构建稳定、高性能的批处理和联机事务系统。脚本语言则强调灵活性、动态性和快速迭代,这与 COBOL 的设计哲学有根本区别。
-
性能需求:COBOL 应用程序通常处理海量数据和高并发事务,对性能和资源利用率有极高要求。传统的解释型脚本语言往往无法满足这些性能指标。
-
生态系统和工具链:COBOL 有一套成熟但封闭的开发、调试和部署工具链。构建一个全新的“CobolScript”生态系统需要巨大的投入。
-
定位不同:COBOL 解决的是核心业务逻辑和数据持久化问题,而脚本语言更多地用于胶水代码、自动化和前端/Web 开发。两者的应用场景和需求差异很大。
7.5 总结“CobolScript”
因此,当你听到“CobolScript”时,最有可能是指在大型机环境中用于管理 COBOL 程序的 JCL,或者是在异构系统之间实现 COBOL 程序与现代脚本语言集成的自动化脚本。它不大可能指向一个独立的、被广泛使用的、与 JavaScript 或 Python 等同类的“CobolScript”编程语言。COBOL 仍然是 COBOL,其核心竞争力在于其强大的业务逻辑处理和海量数据处理能力,而与脚本语言的结合更多体现在其外部接口和自动化管理上。
结论
COBOL 是一门独特且具有历史意义的编程语言,它在过去数十年里一直是全球商业和金融基础设施的基石。其面向业务的语法、强大的数据处理能力、以及在大型机环境中的无与伦比的可靠性,使其在处理核心交易和批处理任务方面依然具有无可替代的价值。尽管它面临着冗长、缺乏现代范式以及人才老化等挑战,但通过面向对象的扩展、与现代技术的集成以及开源社区(如 GnuCOBOL 和 OpenCilk)的努力,COBOL 正在不断演进,以适应21世纪的计算需求。
对于“CobolScript”这一概念,我们将其主要解释为大型机上的 JCL 脚本,以及在跨平台和现代化改造中,现代脚本语言(如 Python)用于与 COBOL 程序进行交互和自动化的场景。COBOL 语言本身保持其编译型的、强类型和面向过程的核心特性,以确保其在关键业务领域的稳定性和性能。在可预见的未来,COBOL 仍将作为“幕后英雄”,默默地支撑着全球经济的运行。
希望这份详尽的描述能帮助你全面理解 COBOL 语言及其相关概念!
posted on 2025-08-21 20:24 gamethinker 阅读(20) 评论(0) 收藏 举报 来源
浙公网安备 33010602011771号