COBOL 是编程语言界的罗德尼·丹泽菲尔德——它没有得到任何尊重。它经常因其冗长而被贬低,并被斥为过时。然而,COBOL 远非一种死语言。据估计,它处理了所有业务交易的 85%,每年编写 50 亿行新的 COBOL 代码。
我做了 10 年的 COBOL 程序员,我不认为它像人们认为的那么糟糕。事实上,它非常擅长处理货币和固定格式的记录。但 COBOL 确实有其怪癖,其中许多怪癖都根植于早期编程的计算环境。这是一个关于穿孔卡片如何吃掉我的程序的故事。
一个神秘的错误
这是一个问题代码的示例,它试图计算订单的运费和预计发货日期
1 identification division.
2 program-id.
3 test-ship.
4
5 data division.
6 working-storage section.
7
8 01 shipping-method pic x(2) value 'US'.
9 01 cust-type pic x(2) value 'EM'.
10 01 normal-ship-date-yyyymmdd pic 9(8) value 20170522.
11 01 nextday-ship-date-yyyymmdd pic 9(8) value 20170508.
12 01 expected-shipping-date pic 9(8).
13 01 shipping-charge pic 99v99 value 4.99.
14
15 procedure division.
16 if shipping-method <> 'FX'
17 move normal-ship-date-yyyymmdd to expected-shipping-date
18 else
19 move nextday-ship-date-yyyymmdd to expected-shipping-date.
20
21 if cust-type = 'EM'
22 move 0 to shipping-charge.
23
24 display expected-shipping-date.
25 display shipping-charge.
即使你以前从未见过 COBOL 代码,这个逻辑也应该很容易理解。如果运输方式是“FX”,客户将获得次日达服务,否则运输需要两周。(这是 90 年代。)员工可以享受免费送货;其他人则支付 4.99 美元。这段代码在我看来是正确的,但它有一个错误——发货日期计算正确,但员工被收取全额运费。
问题原来是第 19 行末尾的句点。那时,追踪到这一点需要一些侦探工作,但现代语法高亮编辑器会立即标记出来。但是为什么这是一个问题?当 COBOL 对第 22 行末尾的句点非常满意时,为什么不喜欢第 19 行的句点呢?
句子而不是代码块
要回答这个问题,我们需要回到 COBOL 在 1950 年代后期的起源。在那之前,大多数语言都是为了解决科学和工程问题而设计的,因此它们的语法类似于数学方程式。(Fortran 是这种类型语言的经典例子。)另一方面,COBOL 旨在用于商业计算。为了让非专业人士更容易学习,格蕾丝·霍珀和她的国防部和 IBM 工程师团队赋予了 COBOL 英语语言语法。与大多数现代语言的递归语法不同,COBOL 程序具有分层结构。COBOL 不是代码块,而是将语句组合成“句子”。就像英语一样,每个句子都以句点结尾。
虽然这在理论上可能看起来是个好主意,但在实践中却被证明是有问题的。这使得移动代码变得困难,因为一个意外的句点可能会意外地终止一个代码块。句点也很难注意到——在 90 年代的 CRT 终端上,它们通常只是一个像素。但这里有一个更深层次的问题,这个问题与程序员在 COBOL 最初开发时编写代码的方式有关。
穿孔卡片

opensource.com
当 COBOL 被设计出来时,硬盘驱动器非常昂贵,因此大多数程序都是在穿孔卡片上编写的。最常见的穿孔卡片由 12x80 的网格组成,其中一个孔代表 1,一个非孔代表 0。每列是一个 12 位的字符,每张卡片是一行 80 个字符的文本。要运行你的程序,你需要将一叠穿孔卡片送入读卡器。每张卡片的前六列和最后八列保留用于序号和标识符。这样,如果你不小心弄掉了你的卡片(这可能是你程序的唯一副本),你可以将卡片通过机械分类器送入,将它们放回正确的顺序。
这意味着 COBOL 忽略了第 72 列之后的任何字符。如果那恰好是一个句点,那么你的代码的整个逻辑都可能改变。而且,正如你现在毫无疑问已经猜到的那样,第 19 行的句点在第 73 列。以下是 COBOL 编译器实际解释这些行的方式
16 if shipping-method <> 'FX'
17 move normal-ship-date-yyyymmdd to expected-shipping-date
18 else
19 move nextday-ship-date-yyyymmdd to expected-shipping-date
20
21 if cust-type = 'EM'
22 move 0 to shipping-charge.
一旦我发现问题所在,修复起来就很容易了:我从第 19 行的开头删除了一个空格字符,这使得句点位于第 72 列。虽然我以前从未遇到过这种情况,但这却是一个非常常见的错误,以至于许多大型机 COBOL 程序员会在他们的终端上在第 72 列和第 73 列之间贴上一根线。
今天的 COBOL
COBOL-85 标准添加了诸如 end-if 之类的范围终止符,因此不再需要句点来结束句子。COBOL 2002 标准允许自由格式代码,尽管许多编译器在此之前很久就支持它了。根据 2002 标准编写的相同代码看起来更像一种现代编程语言
16 if shipping-method <> 'FX'
17 move normal-ship-date-yyyymmdd to expected-shipping-date
18 else
19 move nextday-ship-date-yyyymmdd to expected-shipping-date
20 end-if
21
22 if cust-type = 'EM'
23 move 0 to shipping-charge
24 end-if
请注意,行首的空格也不再是必需的。我通常使用的系统同时支持范围终止符和自由格式代码,因此在我不得不在另一个系统上进行一些更改之前,我从未遇到过这个问题。
对于开源爱好者来说,学习 COBOL 一直很困难。COBOL 编译器传统上是闭源且昂贵的,并且大多数 COBOL 代码都是在企业环境中编写的。然而,一个名为 OpenCOBOL 的开源编译器的开发工作于 2002 年开始。2013 年,它被正式接受为 GNU 软件包并更名为 GnuCOBOL。要了解有关 GnuCOBOL 的更多信息,包括访问 400 页的程序员指南,请访问 该项目的首页。
在沃尔特·曼科夫斯基在费城 FOSSCON 8 月 26 日的演讲 穿孔卡片吃掉了我的程序! 中了解更多关于 COBOL 的信息。
39 条评论