不要在尝试 COBOL 之前就讨厌它

COBOL 就像计算机编程界的罗德尼·丹泽菲尔德,一直不被尊重,但它仍在被使用——并且确实值得尊敬。
485 位读者喜欢这篇文章。
What to like about COBOL

Rainer Gerhards。由 Opensource.com 修改。CC BY-SA 4.0

COBOL 是编程语言界的罗德尼·丹泽菲尔德——它不受任何尊重。它因其冗长而被经常贬低,并被斥为过时。然而,COBOL 远非一种死语言。据估计,它处理了 85% 的商业交易,并且每年编写 50 亿行新的 COBOL 代码。

我做了 10 年的 COBOL 程序员,我不认为它像人们认为的那么糟糕。事实上,它非常擅长处理货币和固定格式记录。但 COBOL 确实有其怪癖,其中许多怪癖都源于早期编程的计算环境。这是一个关于一张穿孔卡片如何吃掉我的程序的故事。

一个神秘的 bug

这是一个问题代码的示例,它试图计算订单的运费和预计发货日期

 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 美元。这段代码在我看来是正确的,但它有一个 bug——发货日期计算正确,但员工被收取全额运费。

问题出在第 19 行末尾的句点。在当时,追踪到这一点需要一些侦探工作,但现代语法高亮编辑器会立即标记出来。但是为什么这是一个问题?为什么 COBOL 不喜欢那个句点,而它对第 22 行末尾的句点却非常满意呢?

句子而不是代码块

要回答这个问题,我们需要回顾 COBOL 在 1950 年代后期的起源。在此之前,大多数语言都是为了解决科学和工程问题而设计的,因此它们的语法类似于数学方程式。(Fortran 是这种类型语言的经典示例。)另一方面,COBOL 旨在用于商业计算。为了使外行人更容易学习,格蕾丝·霍珀和她的国防部及 IBM 工程师团队赋予了 COBOL 英语语言语法。与大多数现代语言的递归语法不同,COBOL 程序具有分层结构。COBOL 没有代码块,而是将语句组合成“句子”。就像在英语中一样,每个句子都以句点结尾。

虽然这在理论上看起来是个好主意,但在实践中却被证明是有问题的。它使得移动代码变得困难,因为一个意外的句点可能会意外地终止一个代码块。句点也很难注意到——在 90 年代的 CRT 终端上,它们通常只是一个像素。但这里存在一个更深层次的问题,这个问题与程序员在 COBOL 首次开发时编写代码的方式有关。

穿孔卡片

COBOL punch card

opensource.com

当 COBOL 被设计出来时,硬盘驱动器非常昂贵,因此大多数程序都是在穿孔卡片上编写的。最常见的穿孔卡片由 12x80 的网格组成,其中一个孔代表 1,一个非孔代表 0。每列是一个 12 位字符,每张卡片是一行 80 个字符的文本。要运行您的程序,您需要将一叠穿孔卡片送入读卡器。每张卡片的前六列和最后八列保留用于序列号和标识符。这样,如果您不小心弄掉了您的卡片组——这可能是您的程序的唯一副本——您可以将卡片通过机械分拣机送入,以将它们放回正确的顺序。

这意味着 COBOL 会忽略第 72 列之后的任何字符。如果第 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 列。虽然我以前从未遇到过这个问题,但这是一个非常常见的 bug,以至于许多大型机 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 页的程序员指南,请访问项目主页

在 Walt Mankowski 的演讲 一张穿孔卡片吃掉了我的程序! 中了解更多关于 COBOL 的信息,该演讲将于 8 月 26 日在费城 FOSSCON 上举行。

标签
User profile image.
Walt Mankowski 是一位从象牙塔计算机科学家转型而来的人,他最近完成了一项博士后研究,与生物学家合作处理和可视化 TB 级的 2D 和 3D 时间推移显微镜图像。在他的过去生活中,他曾在一家大型有线家庭购物网络担任 COBOL 程序员 10 年。他喜欢 Perl、正则表达式、高性能计算和《飞出个未来》。

39 条评论

感谢这篇精彩的文章!我在职业生涯的早期做过一些 COBOL,并且仍然是它的粉丝。

我已经使用了 COBOL-85 标准三十年了,从来没有遇到过句点问题。事实上,我写的每个段落都以 EXIT 语句结尾,并且段落中的最后一个东西是 EXIT 前一行上的句点。

在编写过程部分时要有纪律;在 WORKING-STORAGE SECTION 中要有创新性和条理性,COBOL 可以做到任何事情。

1000-PARAGRAPH.

IF
blah blah blah
END-IF
.
1000-EXIT. EXIT.

:)

只是一个小小的吹毛求疵... 第 72 列是特殊的。如果它包含连字符或“C”,那么编译器应该将下一行附加为单行进行处理。

老实说,我 99% 的时间使用的系统都使用自由格式代码,所以这个问题从来没有出现过。但我在线找到的所有内容都说,延续符是用第 7 列中的“-”表示的,这意味着当前行应该附加到前一行。我找不到任何表明第 72 列被特殊对待的迹象。也许您将它与另一种语言混淆了?

回复 作者 Martin DiViaio (未验证)

抱歉... 但绝对是第 72 列。已经 30 多年了,所以我忘记了它是 COBOL66 的一部分还是 ANSI COBOL 74“标准”。我确实记得有人试图使用它,直到系统管理员告诉他使用更短的变量名并继续前进。

回复 作者 waltman

自 1983 年以来,我一直是 IBM 大型机软件开发人员。特殊的是第 7 列,而不是第 72 列。“-”表示从上一行延续,“*”表示注释行,“/”是编译器指令,用于在编译器列表中强制分页。任何其他内容都不是标准。

回复 作者 Martin DiViaio (未验证)

看来 JCL 和一些大型机汇编程序使用第 72 列来表示延续,所以我猜想某些早期版本的 COBOL 也可能这样做。

回复 作者 Scott Spurlock (未验证)

如果我没记错的话,在 COBOL 刚出现的时候,硬盘驱动器并不存在。大型计算机将数据保存在磁带或纸带上。

IBM 在 1956 年发布了他们的第一个硬盘驱动器 IBM 305 RAMAC。那是在 COBOL 出现之前的几年,COBOL 的历史可以追溯到 1959 年。硬盘驱动器存储非常昂贵,这可能就是它们仍然不常见的原因。

回复 作者 Greg P

当我查找这个时,它似乎确实存在硬盘,但尚不清楚它们是如何使用的,因为它们最初通常只有几 MB 的存储空间。

回复 作者 Greg P

没错。在我的演讲中,我有一张 1956 年工人们移动一个 5 MB 硬盘的照片,那个硬盘像几个冰箱那么大。但是,我用过的第一个 PC 硬盘只有 3 MB 的存储空间,但它看起来已经非常大了!

回复 作者 Greg P

这叫做臃肿软件。今天的电脑和手机都充满了它。

在嬉皮士时代(60 年代和 70 年代),您有时只有 40 KB 的大型机空间来运行程序。
所以您非常努力地使其适应。如果 COBOL 做不到,那么您就用汇编程序来做(至少我们中的一些人是这样做的)。
与今天的后起之秀不同,只有强者才能生存。

回复 作者 Greg P

我在 80 年代早期开始在那些早期的 PC 上编程。当我还是一位象牙塔计算机科学家时,我仍然有一台 PC,但它有 32 个 CPU、64 GB 的 RAM 和 TB 级的存储空间。我们能够处理当时难以想象的计算问题。而且编程已经够难了,不必再微观管理内存。虽然回顾过去很有趣,但它让我更加欣赏现代硬件和软件!

回复 作者 Tony Q. King

在您看到 COBOL 程序在配备 12 个大型磁带驱动器的 4K Remington-Rand Univac-II 上编译之前,您什么都没见过,所有磁带驱动器都在同时旋转。在 1960 年代早期,一个小程序需要大约 1 到 2 个小时才能编译。
但是一旦 IBM 360 在 1965 年左右推出,配备了 2311 磁盘驱动器 - 每个 7 兆字节!- 那个程序将在不到 10 分钟的时间内编译完成。

是的,那是 4K,而不是 4 兆字节。

回复 作者 Greg P

随着 IBM 的 360 系列计算机和操作系统的发布,磁盘驱动器在 60 年代中期变得很常见。对于试图在配备 4K RAM 和 12 个大型磁带驱动器的 Remington-Rand Univac-II 上编译 COBOL 程序的人来说,这是一种受欢迎的解脱。
您应该看看所有这些磁带在编译一个小型 COBOL 程序时的运行情况。通常需要 1 到 2 个小时(自然是在夜间运行)
(嘟囔嘟囔... 诅咒你,格蕾丝·霍珀...!)

** 是的,那是 4 K! 不是 4 MB。

回复 作者 Greg P

很久以前用过 Cobol,我仍然喜欢它!简单易懂。如果我今天想编写一行代码,我必须安装 25 个开源工具和库,破解一些命令行内容,并在一切出现之前祈祷。

当您在第 7 列中放置星号时,它会将该行的其余部分变成注释。你猜怎么着,我有一个注释掉了一个 GOTO 语句。(哎呀)

第 7 列和第 72 列协同工作。第 7 列有几个用途,但它可以与第 72 列结合使用,以表示延续和连接几个涉及国家字符集(解析为非拉丁字符代码的多字节序列)的特殊情况。请参阅: https://www.ibm.com/support/knowledgecenter/en/SS6SGM_3.1.0/com.ibm.aix…

关于穿孔卡片的另一个小观察或琐事。
今天很少有程序员/IT“专家”意识到 80 个字符的文本行(在编辑器或终端屏幕上)来自 120 年前(至少!)由霍勒里斯穿孔卡片,最终被 IBM 数据处理采用,之后编程库最终在 1970 年代进入磁带(短暂地)然后进入磁盘。
唉,赫尔曼·霍勒里斯在 COBOL 甚至还在格蕾丝海军上将的 80 列卡片中打孔之前就去世了。
如果您想为此辩护,您甚至可以争辩说这些穿孔卡片最初是由 18 世纪的纺织织工开发的。

附言:在任何人试图追溯更久远的历史之前
有些人声称穿孔卡片的宽度(或可能是其长度)最初是基于罗马战车车轮的轨道宽度及其在 2000 年前的罗马道路上的特殊矩形凹痕。
胡说八道! 与声称 Saturn-1 火箭助推器的大小基于罗马道路的说法一样,这只是另一个都市传说。 ;-)

回复 作者 Tony Q. King

硬盘驱动器的容量有限和成本与穿孔卡片的使用(在存储程序方面)有关,但还有一个缺乏廉价终端的问题。在 1970 年代后期马里兰大学,学生可以使用的 ASCII 电传打字机数量有限,这意味着大多数学生必须使用穿孔卡片。(当我在那里时,电传打字机被类似 DECwriter 的点阵键盘/打印机取代了。)UNIVAC 大型机的磁鼓已被硬盘取代,但操作系统继续提供鼓 API,该 API 在硬盘上模拟鼓。

我只在 1981 年的数据库课程中使用过一次 COBOL,但我之前几年就读过 Daniel McCracken 关于 COBOL 的经典著作(以及他的关于 Fortran 和 Algol 的著作)。我使用 COBOL 没有任何问题;每种编程语言都需要不同的思维方式,并且(在有限的时间内)了解 COBOL 与了解我使用过的所有其他语言一样在智力上令人满意。

在 1990 年代,我认为,一位 COBOL 布道者过去经常在美国新闻组 USENET 上发帖,要么是 comp.os.unix,要么是 comp.lang.c。他主要关注的不是 COBOL;它只是偶尔出现。在一篇文章中,他提出了一个 *完整*、*可移植* 的 4 行 COBOL 程序来对文件进行排序。(我记得...)可移植性是可能的,因为 COBOL 具有内置的排序功能,而大多数其他语言则没有。在另一种语言中使用特定于操作系统的系统调用来运行外部“sort”程序不是一种可移植的解决方案。

我在 80 年代早期去了宾夕法尼亚大学。我从未使用过穿孔卡片,但仍然有很多旧硬件正在逐步淘汰。但我真正谈论的是 COBOL 的早期。计算机终端直到 70 年代早期才开始可用,而 COBOL 在那时已经使用了十年。电传打字机出现得更早,但我不确定它们与大型机配合使用效果如何,因为我见过的所有电传打字机都需要 bisync 连接。

今天,当我用 C++、Perl 或 Python 等语言编程时,我经常想知道是否有更好的方法可以编写某些东西。我应该使用哈希还是对象?结构还是类?循环还是列表推导式?我从不在 COBOL 中这样做,因为通常只有一种方法可以做任何事情。它可能不是一个好方法,但这就是您做事的方式。因此,我大部分精力都用于解决业务问题,而不是语言问题。当然,当您知道有很多更好的方法来做事时,也会感到沮丧,但有时缺乏选择可以帮助集中注意力。 :)

回复 作者 Alex Measday (未验证)

很棒的文章。我很高兴看到 COBOL 仍然活着并被大量使用。

我是一家仍然存在的主要大型机公司的 COBOL 编译器开发人员。

其中一家大型机公司?您的意思是 Amdahl 和 Hitachi 仍然在游戏中?
45-50 年前,只有“白雪公主”和七个小矮人。
如果您能告诉我那可怜的七个的名字,那就额外加分!

回复 作者 User

一个问题... 哪个版本的 COBOL 向 IF 语句添加了 ELSE 子句?

我曾经在一个 DBA 团队中工作,该团队必须使用不支持 ELSE 子句的 COBOL 从大型机数据库中提取和转换 Y2K 的数据。我们称之为“COBOL I”,但我找不到使用该名称的在线参考。

这太奇怪了!我不能说我听说过任何不支持 ELSE 的编程语言。如果您想做等效于 ELSE 的事情,您是否必须使用条件的否定重复 IF?您的 COBOL 是在大型机上还是在其他平台上运行的?

回复 作者 DocSalvager

Jean Sammet 将“强大的条件(即 IF THEN ELSE)结构”列为第一个版本的 COBOL COBOL '60 的核心贡献之一。(参见 Jean Sammet 的“COBOL 的早期历史”,第 199-243 页,编程语言历史 I (1978))。我想知道您遇到的版本是否存在特定的实现问题。

回复 作者 DocSalvager

不,问题是第 73 列中的句点被忽略了。我第一次在 Stratus VOS 编译器上看到它,并且我能够在 GnuCOBOL 中复制它。它会发生在任何控制结构上。

回复 作者 Jeff B (未验证)

但是 GnuCOBOL 没有像旧编译器那样,而是说“第 72 列之后的源代码文本” :-)

回复 作者 waltman

那是我的第一篇文章,我没有看到编辑它的方法。我的意思是,哪个是最后一个不支持 ELSE 子句的版本?

我仍然使用 Cobol。我所有的业务程序都在 Cobol 中。刚刚完成一个新程序的开发。GUI 界面,MySQL 数据库。最初在 IBM 360 上开始,然后在 1970 年代的 DEC10 上开始。然后是 TRS80 model II 和 III,最后是 PC。当前的服务器是 Linux Debian,带有 WIN10 用户机器。运行良好且快速。

好文章。我在 80 年代在大学学习了 COBOL,并在 90 年代早期做了一些 COBOL 编程。我不能说我喜欢它,但它完成了工作。
您提到 COBOL“处理了估计 85% 的商业交易”。这个估计有具体的来源吗?
谢谢

很高兴在 cobol 代码中看到小写字母。

我几十年前就这么做了;没有其他人。 很高兴发现代码编译得很好。 受不了所有大写字母,因为它总是对你大喊大叫。 呜。

我们的代码库都是小写的。 也许他们那样做是因为我们在 Stratus 计算机上,而不是大型机上? 我从来没有想过问任何人。

回复 作者 dwight (未验证)

我有很多 Cobol 编程工作。 我还使用穿孔卡片作为编程工作的输入。 最初,语句被键入并打孔,然后像其中一个博客中显示的那样读取读卡器。 我在 PDP/11 上完成了所有业务应用程序。
编写代码非常容易,甚至我的心都倾向于 Cobol。 后来,我在 1984 年至 1994 年间在中东为 IBM 计算机工作时,编写了 MF Cobol、RM Cobol、MS Cobol 程序 10 年。 我还在 AS/400 Cobol/400 中编写了几个程序。 我想即使在今天我也很乐意编写 Cobol 代码。 祝你好运。我在 Cobol 中做得很好

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.