简介:面向正在完成 COBOL 课程大作业的计算机专业学生,这份实践汇总以公司销售统计程序为主线,完整呈现从需求设计、数据文件构造到程序编码与报表输出的过程。内容覆盖 COBOL 基础语法、READ/WRITE 文件处理、表处理、异常数据检测,以及 SORT/MERGE 排序合并操作,对理解商业数据处理流程和准备实验报告都有直接帮助。资源共 1 个 PDF 文件,压缩包约 426KB,正文包含实验目的、设计步骤、关键代码段和输入数据示例,适合对照学习。目前已有 138 人浏览学习,作为大作业参考,可作为编写类似报表程序的起步模板。
1. 大型机COBOL练习汇总:先看懂这份材料在讲什么
很多做COBOL大作业的同学,第一眼看到“练习汇总.pdf”这类文件,会下意识去找答案。但作为常年跟大型机打交道的人,我想先泼一盆冷水:COBOL大作业的真正难点从来不在语法,而在于你“不知道作业怎么跑进大型机”。语法错一眼能看见,JCL写错、数据集不存在、编译选项不对,才是让人卡住两三天的地方。
这份练习汇总大概率分成三块:语法填空题、小功能程序题(报表、排序、文件更新)和综合题(结合JCL提交、看SYSOUT输出)。它解决的是“从会写COBOL到能在真实大型机上把作业提交出结果”的过渡。适合的读者是刚拿到课程账号、还没碰过TSO/ISPF、以及在外包或银行项目里要快速接手存量COBOL程序的人。下面按一条完整路径来讲:环境怎么搭、语法题怎么做、JCL怎么绕开坑、输出怎么验证。
2. 搭一套能写能跑的大型机COBOL环境
2.1 先用GnuCOBOL在本地跑通最小程序
在校或自学阶段,没人能保证你随时有大型机账号。常见做法是先在本地用GnuCOBOL验证逻辑,再把代码平移到大型机。它是开源编译器,语法上支持到COBOL-85和部分COBOL-2002,做课程练习足够。
Debian/Ubuntu系安装:
sudo apt install gnucobolRed Hat/CentOS系:
sudo yum install gnucobol安装完后可以顺手确认版本:
cobc -V写一个最小程序,存成HELLO.CBL:
IDENTIFICATION DIVISION. PROGRAM-ID. HELLO. PROCEDURE DIVISION. DISPLAY 'HELLO MAINFRAME COBOL'. STOP RUN.编译加运行:
cobc -x -free -o hello hello.cbl ./hello参数说明:-x表示生成可执行文件;-free表示启用自由格式代码,可以不用从第8列开始写。课程作业如果按大型机默认边界要求,就用固定格式,这时把-free去掉,正文写在7到72列之间。本地工具链的目的不是替代大型机,而是让你在报错之前先确认算法和数据结构本身没有低级错误。
2.2 大型机环境不是“下载一下”就能搞定的事
很多初学者搜“ibm大型机操作教程”,会期望找到一个可以安装到笔记本上的“大型机模拟器”。这里给一个边界:z/OS本身并没有像Oracle那样公开的免费开发版可下载。学校课程通常提供z/OS账号,通过TN3270客户端(如IBM Personal Communications或开源客户端)连接TSO/E和ISPF。真正在做生产的单位,走的是企业内部开发环境,或者IBM Wazi这样的云开发平台。
有人会搜“大型机下载cmip”,需要澄清一下:cmip并不是像Eclipse那样能从官网拿到的IDE安装包,它涉及的是大型机环境里系统管理或配置管理接口层面的东西。对做COBOL练习而言,你真正需要的是能打开ISPF编辑器提交JCL的入口,而不是本地先装某个管理协议。
典型连接流程是:打开TN3270客户端 -> 输入TSO登录 -> 进ISPF -> 用Option 3.4看数据集列表 -> 用Option 2编辑代码 -> 用Option 6提交JCL。整个过程不需要下载任何“大型机本体”,你只是登录到已有的系统上。
2.3 本地和大型机之间的三类差异
在本地编译通过,不等于到大型机上就能一次成功。最常见的差异有三个。
第一是编译选项差异。大型机上用IGYCRCTL或IGYCLG这个编译器程序,需要显式指定LIB、OBJECT、DYNAM等参数;本地GnuCOBOL默认行为不完全对齐。
第二是文件系统。大型机里你面对的QSAM、VSAM文件是“数据集”,有DSN名字、有record format(FB、VB)、有LRECL;本地GnuCOBOL用的是普通文件路径。一个SELECT ... ASSIGN TO SALESDD程序,在大型机里要靠JCL里的DD语句把SALESDD指向真正数据集。
第三是字符集。大型机常见EBCDIC编码,PC上是ASCII。如果代码里直接写入中文字符或者依赖9这一类的数值编辑,两边显示和比较结果会不一致。练习里字符串比较题,最好只用大写英文字母和数字。
3. 练习里最常考的语法点:从控制断点到表处理
3.1 报表题:每份作业里都有的控制断点
大作业题目里出现频率最高的是“读一个顺序文件,按部门汇总金额并输出报表”。这类题考的是WORKING-STORAGE定义、MOVE/ADD基本语句、AT END判断、控制断点写法。一个核心套路是:记录有序排列时,当前键值一变化,就先打印上一组的小计,再开始新一组累计。
下来看一个可直接跑的GnuCOBOL示例,假设输入记录每行格式为“区域号 产品名 金额”:
IDENTIFICATION DIVISION. PROGRAM-ID. RPT01. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT SALES-FILE ASSIGN TO SALESDD. SELECT REPORT-FILE ASSIGN TO RPTDD. DATA DIVISION. FILE SECTION. FD SALES-FILE. 01 SALES-REC. 05 REGION PIC X(02). 05 PRODUCT PIC X(10). 05 AMOUNT PIC 9(05)V99. FD REPORT-FILE. 01 REPORT-REC PIC X(80). WORKING-STORAGE SECTION. 01 WS-EOF PIC X(01) VALUE 'N'. 01 WS-PREV-REGION PIC X(02). 01 WS-REGION-TOT PIC 9(07)V99 VALUE ZERO. 01 WS-GRAND-TOT PIC 9(07)V99 VALUE ZERO. 01 WS-LINE PIC X(80). PROCEDURE DIVISION. MAIN-PARA. OPEN INPUT SALES-FILE OUTPUT REPORT-FILE. MOVE SPACES TO WS-PREV-REGION. PERFORM UNTIL WS-EOF = 'Y' READ SALES-FILE AT END MOVE 'Y' TO WS-EOF NOT AT END PERFORM HANDLE-REC END-READ END-PERFORM. PERFORM WRITE-REGION-TOT. PERFORM WRITE-GRAND. CLOSE SALES-FILE REPORT-FILE. STOP RUN. HANDLE-REC. IF REGION NOT = WS-PREV-REGION IF WS-PREV-REGION NOT = SPACES PERFORM WRITE-REGION-TOT END-IF MOVE REGION TO WS-PREV-REGION END-IF. ADD AMOUNT TO WS-REGION-TOT WS-GRAND-TOT. WRITE-REGION-TOT. STRING 'REGION ' WS-PREV-REGION ' TOTAL: ' WS-REGION-TOT DELIMITED BY SIZE INTO WS-LINE. WRITE REPORT-REC FROM WS-LINE. MOVE ZERO TO WS-REGION-TOT. WRITE-GRAND. STRING 'GRAND TOTAL: ' WS-GRAND-TOT DELIMITED BY SIZE INTO WS-LINE. WRITE REPORT-REC FROM WS-LINE.逻辑说明:HANDLE-REC每次读入一条记录后,先比较当前REGION和上一组REGION是否不同,不同就触发上一组的汇总输出,然后更新当前组号。AT END置为'Y',退出PERFORM UNTIL循环。最后在关闭文件前补打最后一组小计和总计。
参数说明:PIC 9(05)V99中V只是隐含小数点,不占用存储空间,所以和6字节的数值做ADD时不会报长度不匹配;报表输出时要用PIC 9(07)V99做累加变量,避免金额溢出。STRING语句里DELIMITED BY SIZE表示将整个数据项拼到目标变量中。
3.2 文件处理题:顺序读、排序与合并
另一类高频题型是文件合并,比如两个已排序的客户主文件,按客户号合并成一个新文件。它不靠SORT语句,而是靠手工归并,这是练习里拉分的关键。核心是维护两个文件的EOF状态,谁小谁先写。
IDENTIFICATION DIVISION. PROGRAM-ID. MERGE01. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT FILE-A ASSIGN TO FILEADD. SELECT FILE-B ASSIGN TO FILEBDD. SELECT FILE-OUT ASSIGN TO OUTDD. DATA DIVISION. FILE SECTION. FD FILE-A. 01 REC-A. 05 CUST-A PIC X(06). 05 NAME-A PIC X(20). FD FILE-B. 01 REC-B. 05 CUST-B PIC X(06). 05 NAME-B PIC X(20). FD FILE-OUT. 01 REC-OUT PIC X(80). WORKING-STORAGE SECTION. 01 WS-EOF-A PIC X(01) VALUE 'N'. 01 WS-EOF-B PIC X(01) VALUE 'N'. 01 WS-STAT PIC X(40). PROCEDURE DIVISION. MAIN. OPEN INPUT FILE-A FILE-B OUTPUT FILE-OUT. PERFORM READ-A. PERFORM READ-B. PERFORM UNTIL WS-EOF-A = 'Y' AND WS-EOF-B = 'Y' IF WS-EOF-A NOT = 'Y' AND (WS-EOF-B = 'Y' OR CUST-A <= CUST-B) MOVE REC-A TO WS-STAT MOVE WS-STAT TO REC-OUT WRITE REC-OUT PERFORM READ-A ELSE MOVE REC-B TO WS-STAT MOVE WS-STAT TO REC-OUT WRITE REC-OUT PERFORM READ-B END-IF END-PERFORM. CLOSE FILE-A FILE-B FILE-OUT. STOP RUN. READ-A. READ FILE-A AT END MOVE 'Y' TO WS-EOF-A. READ-B. READ FILE-B AT END MOVE 'Y' TO WS-EOF-B.逻辑说明:每次循环只处理较小的一方,处理完立刻读下一条。CUST-A <= CUST-B保证两边相同时只取A,不丢记录也不重复。这里的WS-STAT是一个PIC X(40)的缓冲,把结构化记录搬进X类型变量后再搬到输出变量,是为了避免不同类型的记录直接MOVE可能出现的编译警告,这也是大型机作业里常见的规范写法。
参数说明:AT END只作用于当前READ,不能写成AT END MOVE 'Y' TO WS-EOF-A WS-EOF-B,因为一次READ只有目标文件有EOF。<=是COBOL中对“小于等于”的标准写法,不能用<=之外的形式。
3.3 表处理题:OCCURS 和 INDEXED BY 的下标边界
练习里还会有一类,给定一个月份号,返回对应的季度名。这类题不能反复写IF判断,标准做法是查表。COBOL支持两类表访问:下标(SUBSCript)和索引(INDEXED BY)。下标直接用数字变量定位;索引需要先用SET定位再通过SEARCH ALL二分查找。
IDENTIFICATION DIVISION. PROGRAM-ID. TABLEDEMO. DATA DIVISION. WORKING-STORAGE SECTION. 01 MONTH-INPUT PIC 9(02). 01 TXT-OUT PIC X(20). 01 QTR-TABLE. 05 QTR-SEASON OCCURS 4 INDEXED BY Q-IX. 10 QTR-MAX PIC 9(02). 10 QTR-NAME PIC X(10). 01 MONTH-IND PIC 9(01). PROCEDURE DIVISION. MAIN. MOVE 2 TO MONTH-INPUT. PERFORM FIND-QUARTER DISPLAY TXT-OUT. STOP RUN. FIND-QUARTER. SET Q-IX TO 1. SEARCH QTR-SEASON AT END MOVE 'UNKNOWN' TO TXT-OUT WHEN MONTH-INPUT <= QTR-MAX(Q-IX) MOVE QTR-NAME(Q-IX) TO TXT-OUT.运行时初始化QTR-TABLE是必要的,练习里有人漏了OCCURS之前给表赋初值,结果输出乱码。你可以用VALUE子句为表中各元素赋初值,也可以在主程序开头用MOVE '03JANQ01...'一类的常量灌入。像上面这段只定义结构不赋初值时会显示空白或异常,实际练习里记得补上PERFORM VARYING循环去填充。
参数说明:INDEXED BY定义的索引不是普通数据项,不能直接查看或加减,必须用SET语句初始化。SEARCH ALL需要表按升序排列,否则二分算法结果无定义。若只是练习查表细节,可以退回去用PERFORM VARYING + 下标逐行查。
4. 让大型机COBOL作业跑出结果的JCL写法
4.1 一个裸奔作业的最小JCL示例
COBOL程序在大型机上不是直接跑鸟文件,而是靠JCL提交。JCL对COBOL练习来说不是附加题,而是把作业从“代码”变成“输出”的唯一通道。下面是一个将编译和运行合成两步的JCL样本(用IGYCLG在一个JOB内完成编译链接并运行):
//COBOLJ JOB (ACCT),'COBOL REPORT',CLASS=A,MSGCLASS=H // MSGLEVEL=(1,1),REGION=128M //* //RUNRPT EXEC IGYCLG, // PARM.COBOL='LIB,OBJECT,NODYNAM' //SETUP DD DISP=SHR,DSN=MY.PROD.LOADLIB //* //COBOL.SYSUT1 DD UNIT=SYSDA,SPACE=(CYL,(1,1)) //COBOL.SYSUT2 DD UNIT=SYSDA,SPACE=(CYL,(1,1)) //COBOL.SYSUT3 DD UNIT=SYSDA,SPACE=(CYL,(1,1)) //COBOL.SYSIN DD DSN=MY.SOURCE.COBOL(RPT01),DISP=SHR //COBOL.SYSPRINT DD SYSOUT=* //* //GO.SALESDD DD DSN=MY.INPUT.SALES,DISP=SHR //GO.RPTDD DD SYSOUT=* //GO.SYSOUT DD SYSOUT=* //GO.SYSUDUMP DD SYSOUT=* //GO.CEEDUMP DD SYSOUT=*说明:EXEC IGYCLG是IBM提供的批处理过程,内部依次执行编译、链接、运行。PARM.COBOL里的OBJECT表示生成目标代码,LIB启用标准库搜索,NODYNAM关闭动态调用。GO.SALESDD和GO.RPTDD是给程序中两个SELECT子句对应的DDNAME喂数:SALESDD指向输入数据集,RPTDD直接打到SYSOUT输出。
参数说明:MSGLEVEL=(1,1)让JCL里同时回显JOB语句和每条DD;REGION=128M指定地址空间可以用的内存上限,COBOL程序如果内部开大表,REGION给太小会直接S0C4或S322。CLASS=A是作业队列优先级,不同系统定义不同,最好查看当前系统的JES定义。
4.2 初学者最容易踩的四个JCL坑
第一个坑是数据集不存在而直接DISP=SHR。代码如下时,即使程序逻辑正确,运行也会以ABEND结束:
//GO.SALESDD DD DSN=MY.INPUT.SALES,DISP=SHR若该数据集尚未创建,会报JCL错误。第一次作业可以先创建一个临时的顺序数据集,比如:
//DD1 DD DSN=MY.INPUT.SALES, // UNIT=SYSDA,DISP=(NEW,KEEP), // SPACE=(TRK,(10,1),RLSE), // DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)第二个坑是COBOL记录的LRECL与数据集实际记录长度不匹配。PIC X(80)的记录配合FB/80没问题,但如果你定义了PIC X(100)而LRECL是80,读文件时会出现逻辑记录的截断和S0C7数值错误。任何文件读操作之前,都应该先确认DCB=(RECFM,LRECL,BLKSIZE)三个属性。
第三个坑是GO.SYSPRINT没加。程序中如果用了DISPLAY语句,输出默认写到SYSPRINT。不在JCL里定义这个DD,DISPLAY内容会直接进作业的JES消息,在ISPF 3.8里查看时非常难定位。建议未加就养成把SYSPRINT DD SYSOUT=*写上的习惯。
第四个坑是覆盖了受保护成员。在大机构里,数据集MY.PROD.LOADLIB可能被加上了RACF保护,普通学生账号不能写入,结果在链接阶段报权限错误。练习阶段最好用自己名字缩写定义的DSN,避免去写共享库。
4.3 关于“大型机cmip”和操作教程的常见混淆
现在网上一搜“大型机操作教程”,会出现一堆TSO命令清单,但真正上手几率很低。还有人搜“大型机下载cmip”,这类词的歧义很大。如果你看到“cmip”出现,不要把它当作普通PC上可以下载的安装包。在真实环境中,大型机设备上的系统管理、运维接口往往有着严格的访问控制体系,类似“配置文件管理接口”这种层面不是课程作业该碰的东西。换一句更直接的话:课程练习阶段,你需要的只是ISPF编辑器、JCL,和一个能读JES输出(SYSOUT)的作业入口。不要浪费时间去找某种“万能模拟器”工具,也不要在自己笔记本上部署企业级生产模拟环境,那是另一个层次的工程问题。
5. 练习汇总的加速技巧:利用SYSOUT和S0C7反向定位
拿到一份几十页的练习汇总,与其按顺序做,不如反向来:先用JCL把每个作业的“输出”模式固定下来。每道题目都提前写好一个运行JCL,占位输出设为SYSOUT,然后用SD(System Display)手段验证。我在做课程辅导时,会让同学把下面这十条命令抄到ISPF Option 6的命令行里反复用:
TSO LOGON ISPF 3.4 -> 数据集的列表、查找自己的DSN ISPF 3.8 -> 查看JOB状态与JCL错误 ISPF 2 -> 直接编辑代码 ISPF 6 -> 提交JCL当一次运行后得到S0C7,SSN信息会写在SYSOUT中,可直接在3.8中打开该Job的JESMSGLG。关键看两点:一是 ABEND 码在JOB开头还是某个STEP里,二是COBOL.SYSPRINT可能已经给出了具体行号。搜索S0C7的调试资料时,要记住80%的情况是COBOL里数值字段被非数字内容覆盖,原因不外乎:把字母读到数值项、记录长度错位、或INDEX越界。找到离异常最近的一次MOVE,问题基本就解决了。
另一个实用技巧是直接在ISPF编辑模式下,用C和A行命令复制“上一道题的JCL模板”,只改DSN和PGM名。做练习汇总时每次新开JCL会很烦躁,而题目之间差异往往只有程序名和数据集名。按C复制整块,在目标位置按A插入后,用CHANGE PGM=RPT01 PGM=RPT02 ALL全局替换,省下大量手工输入时间。
如果作业要求输出推送到某个普通文件而不是SYSOUT,可以先把RPTDD改成临时数据集,运行结束后用3.4查看。注意:REPORT-LINE等打印行能正常显示的前提是RECORDING MODE F,如果在打开报表时用了VARIABLE,那么往往LRECL会比设定值多出4字节的前缀。这个4字节通常是RDW(Record Descriptor Word),初学者最容易忽略。判断方法很简单:把输出数据集用HEX模式查看,如果看到每行开头多出0000 0050一类的字节,说明你正处在一个变长记录陷阱里。要么改DCB为RECFM=F,要么调整程序的FD定义,两种做法二选一即可。
本文还有配套的精品资源,点击获取