在你尝试之前不要讨厌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 确实有它的怪癖,其中许多怪癖都植根于早期编程的计算环境中。这是一个关于穿孔卡片如何吃掉我的程序的故事。

一个神秘的错误

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

 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 行末尾的那个句点却非常满意?

句子而不是代码块

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

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

穿孔卡片

COBOL punch card

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 页的程序员指南,请访问该项目的主页

在沃尔特·曼科夫斯基在费城 8 月 26 日举行的 FOSSCON 会议上的演讲“穿孔卡片吃掉了我的程序!”中了解更多关于 COBOL 的信息。

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

39 条评论

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

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

在你编写过程划分代码时要自律;在你的 WORKING-STORAGE SECTION 中要有创新性和条理性,那么用 COBOL 就没有什么你做不到的。

1000-段落。

如果
啦啦啦
结束-如果
.
1000-退出。退出。

:)

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

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

回复 作者 马丁·迪维艾奥 (未验证)

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

回复 作者 waltman

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

回复 作者 马丁·迪维艾奥 (未验证)

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

回复 作者 斯科特·斯普尔洛克 (未验证)

如果我从 1960 年代正确地回忆起来,在 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

磁盘驱动器在 60 年代中期随着 IBM 的 360 系列计算机和操作系统的发布而变得普遍。对于试图在配备 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 年(至少!)历史的霍勒里斯穿孔卡片的 80 列,该卡片最终被 IBM 数据处理部门采用,然后在 1970 年代编程库最终将其放在磁带(短暂地)上,然后放在磁盘上。
可惜,赫尔曼·霍勒里斯在 COBOL 甚至成为格蕾丝海军上将 80 列卡片上的一个穿孔之前就去世了。
如果你想为此辩护,你甚至可以争辩说这些穿孔卡片最初是由 18 世纪的纺织织工开发的。

附言:在任何人试图进一步追溯历史之前
有些人声称,穿孔卡片的宽度(或长度)最初是基于罗马战车车轮的轨道宽度以及 2000 年前罗马道路上特有的矩形凹痕。
胡说八道!正如土星 1 号火箭助推器的尺寸是基于罗马道路的说法一样,这只是另一个都市传说。;-)

回复 作者 Tony Q. King

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

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

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

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

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

回复 作者 亚历克斯·梅斯代 (未验证)

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

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

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

回复 作者 用户

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

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

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

回复 作者 DocSalvager

简·萨梅特将“强大的条件(即 IF THEN ELSE)结构”列为 COBOL 的第一个版本 COBOL '60 的核心贡献之一。(请参阅简·萨梅特的“COBOL 的早期历史”,编程语言历史 I (1978) 第 199-243 页。)我想知道你遇到的版本是否在具体实现方面遇到了问题。

回复 作者 DocSalvager

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

回复 作者 杰夫·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 计算机,而不是大型机?我从来没有想过问任何人。

回复 作者 德怀特 (未验证)

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

Creative Commons License本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.